summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Shell/Android.bp30
-rw-r--r--libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml20
-rw-r--r--libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml20
-rw-r--r--libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml20
-rw-r--r--libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml20
-rw-r--r--libs/WindowManager/Shell/res/color/compat_background_ripple.xml (renamed from libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml)0
-rw-r--r--libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml23
-rw-r--r--libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml21
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml33
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml20
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml32
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml20
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml53
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml20
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_dialog_background.xml21
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background.xml21
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml20
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_ic_letterboxed_app.xml29
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml32
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_ic_screen_rotation.xml26
-rw-r--r--libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml32
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml25
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml25
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml25
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml25
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml27
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml2
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml (renamed from libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml)7
-rw-r--r--libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml22
-rw-r--r--libs/WindowManager/Shell/res/layout/background_panel.xml26
-rw-r--r--libs/WindowManager/Shell/res/layout/badged_image_view.xml55
-rw-r--r--libs/WindowManager/Shell/res/layout/compat_mode_hint.xml4
-rw-r--r--libs/WindowManager/Shell/res/layout/compat_ui_layout.xml38
-rw-r--r--libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml40
-rw-r--r--libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml99
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu.xml129
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml48
-rw-r--r--libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml21
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml33
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml19
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml17
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml17
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml19
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml23
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-tvdpi/dimen.xml16
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml13
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings_tv.xml1
-rw-r--r--libs/WindowManager/Shell/res/values/attrs.xml22
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml4
-rw-r--r--libs/WindowManager/Shell/res/values/colors_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml21
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml28
-rw-r--r--libs/WindowManager/Shell/res/values/strings_tv.xml3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java156
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java253
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java360
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java79
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java121
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java99
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java90
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java91
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java82
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java356
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java364
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java75
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java138
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java246
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java272
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java106
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java430
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java69
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java142
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipInterpolators.java47
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java95
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java269
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java173
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java182
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java68
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java151
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java732
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java68
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java104
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java72
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java84
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java109
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java528
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java156
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java63
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java21
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTest.xml12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt19
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt41
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt23
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt79
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt48
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt46
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt36
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt39
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt37
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt33
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt28
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt51
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java123
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java182
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java114
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java112
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java104
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java232
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java61
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java131
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java133
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java17
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java7
-rw-r--r--libs/androidfw/AssetManager2.cpp63
-rw-r--r--libs/androidfw/LoadedArsc.cpp22
-rw-r--r--libs/androidfw/Locale.cpp43
-rw-r--r--libs/androidfw/ResourceTypes.cpp23
-rw-r--r--libs/androidfw/TEST_MAPPING3
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h12
-rw-r--r--libs/androidfw/tests/AssetManager2_test.cpp8
-rw-r--r--libs/androidfw/tests/Config_test.cpp3
-rw-r--r--libs/androidfw/tests/PosixUtils_test.cpp4
-rw-r--r--libs/hwui/Android.bp9
-rw-r--r--libs/hwui/ColorMode.h2
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp39
-rw-r--r--libs/hwui/DeferredLayerUpdater.h4
-rw-r--r--libs/hwui/DisplayListOps.in1
-rw-r--r--libs/hwui/FrameMetricsObserver.h28
-rw-r--r--libs/hwui/FrameMetricsReporter.cpp56
-rw-r--r--libs/hwui/FrameMetricsReporter.h23
-rw-r--r--libs/hwui/HardwareBitmapUploader.cpp51
-rw-r--r--libs/hwui/HardwareBitmapUploader.h2
-rw-r--r--libs/hwui/JankTracker.cpp32
-rw-r--r--libs/hwui/JankTracker.h3
-rw-r--r--libs/hwui/Layer.cpp6
-rw-r--r--libs/hwui/Layer.h36
-rw-r--r--libs/hwui/Outline.h10
-rw-r--r--libs/hwui/ProfileData.cpp1
-rw-r--r--libs/hwui/Readback.cpp23
-rw-r--r--libs/hwui/Readback.h3
-rw-r--r--libs/hwui/RecordingCanvas.cpp25
-rw-r--r--libs/hwui/RecordingCanvas.h2
-rw-r--r--libs/hwui/RenderProperties.h6
-rw-r--r--libs/hwui/SkiaCanvas.cpp55
-rw-r--r--libs/hwui/SkiaCanvas.h26
-rw-r--r--libs/hwui/VectorDrawable.cpp10
-rw-r--r--libs/hwui/VectorDrawable.h2
-rw-r--r--libs/hwui/WebViewFunctorManager.cpp39
-rw-r--r--libs/hwui/WebViewFunctorManager.h1
-rw-r--r--libs/hwui/apex/LayoutlibLoader.cpp42
-rw-r--r--libs/hwui/apex/android_bitmap.cpp7
-rw-r--r--libs/hwui/apex/include/android/graphics/renderthread.h34
-rw-r--r--libs/hwui/apex/renderthread.cpp25
-rw-r--r--libs/hwui/canvas/CanvasFrontend.cpp97
-rw-r--r--libs/hwui/canvas/CanvasFrontend.h76
-rw-r--r--libs/hwui/effects/StretchEffect.cpp2
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.cpp1
-rw-r--r--libs/hwui/hwui/BlurDrawLooper.cpp2
-rw-r--r--libs/hwui/hwui/BlurDrawLooper.h10
-rw-r--r--libs/hwui/hwui/Canvas.h10
-rw-r--r--libs/hwui/hwui/ImageDecoder.cpp3
-rw-r--r--libs/hwui/hwui/MinikinUtils.cpp10
-rw-r--r--libs/hwui/hwui/MinikinUtils.h4
-rw-r--r--libs/hwui/hwui/Paint.h17
-rw-r--r--libs/hwui/hwui/PaintFilter.h5
-rw-r--r--libs/hwui/hwui/PaintImpl.cpp32
-rw-r--r--libs/hwui/jni/AnimatedImageDrawable.cpp55
-rwxr-xr-xlibs/hwui/jni/Bitmap.cpp2
-rw-r--r--libs/hwui/jni/BitmapRegionDecoder.cpp11
-rw-r--r--libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp10
-rw-r--r--libs/hwui/jni/GIFMovie.cpp11
-rw-r--r--libs/hwui/jni/Graphics.cpp32
-rw-r--r--libs/hwui/jni/GraphicsJNI.h21
-rw-r--r--libs/hwui/jni/NinePatch.cpp2
-rw-r--r--libs/hwui/jni/Paint.cpp90
-rw-r--r--libs/hwui/jni/PaintFilter.cpp4
-rw-r--r--libs/hwui/jni/RenderEffect.cpp45
-rw-r--r--libs/hwui/jni/Shader.cpp121
-rw-r--r--libs/hwui/jni/Utils.cpp21
-rw-r--r--libs/hwui/jni/YuvToJpegEncoder.cpp4
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp72
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp85
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp31
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRendererObserver.h3
-rw-r--r--libs/hwui/jni/android_graphics_RenderNode.cpp94
-rw-r--r--libs/hwui/jni/text/MeasuredText.cpp30
-rw-r--r--libs/hwui/jni/text/TextShaper.cpp1
-rw-r--r--libs/hwui/libhwui.map.txt3
-rw-r--r--libs/hwui/pipeline/skia/DumpOpsCanvas.h2
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp187
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp4
-rw-r--r--libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp2
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp4
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp24
-rw-r--r--libs/hwui/pipeline/skia/VkFunctorDrawable.cpp1
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp2
-rw-r--r--libs/hwui/private/hwui/DrawVkInfo.h3
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp112
-rw-r--r--libs/hwui/renderthread/CanvasContext.h61
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp12
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h13
-rw-r--r--libs/hwui/renderthread/EglManager.cpp136
-rw-r--r--libs/hwui/renderthread/EglManager.h2
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp12
-rw-r--r--libs/hwui/renderthread/RenderProxy.h5
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp69
-rw-r--r--libs/hwui/renderthread/RenderThread.h9
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp3
-rw-r--r--libs/hwui/renderthread/VulkanSurface.cpp13
-rw-r--r--libs/hwui/tests/common/TestUtils.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/PathClippingAnimation.cpp153
-rw-r--r--libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp31
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp2
-rw-r--r--libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp5
-rw-r--r--libs/hwui/tests/unit/FrameMetricsReporterTests.cpp211
-rw-r--r--libs/hwui/tests/unit/JankTrackerTests.cpp35
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp2
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp6
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp4
-rw-r--r--libs/hwui/utils/Color.cpp8
-rw-r--r--libs/hwui/utils/PaintUtils.h11
-rw-r--r--libs/input/Android.bp1
-rw-r--r--libs/input/PointerController.cpp148
-rw-r--r--libs/input/PointerController.h50
-rw-r--r--libs/input/SpriteController.cpp29
-rw-r--r--libs/input/SpriteController.h8
-rw-r--r--libs/input/tests/PointerController_test.cpp32
-rw-r--r--libs/input/tests/mocks/MockSpriteController.h3
-rw-r--r--libs/services/Android.bp1
497 files changed, 11587 insertions, 3994 deletions
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index cdff5858c77d..e9b3c4990ef2 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -43,7 +43,7 @@ filegroup {
name: "wm_shell_util-sources",
srcs: [
"src/com/android/wm/shell/util/**/*.java",
- "src/com/android/wm/shell/common/split/SplitScreenConstants.java"
+ "src/com/android/wm/shell/common/split/SplitScreenConstants.java",
],
path: "src",
}
@@ -74,13 +74,13 @@ genrule {
],
tools: ["protologtool"],
cmd: "$(location protologtool) transform-protolog-calls " +
- "--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " +
- "--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " +
- "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
- "--loggroups-jar $(location :wm_shell_protolog-groups) " +
- "--output-srcjar $(out) " +
- "$(locations :wm_shell-sources)",
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " +
+ "--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " +
+ "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+ "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+ "--output-srcjar $(out) " +
+ "$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.srcjar"],
}
@@ -92,13 +92,14 @@ genrule {
],
tools: ["protologtool"],
cmd: "$(location protologtool) generate-viewer-config " +
- "--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
- "--loggroups-jar $(location :wm_shell_protolog-groups) " +
- "--viewer-conf $(out) " +
- "$(locations :wm_shell-sources)",
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+ "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+ "--viewer-conf $(out) " +
+ "$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.json"],
}
+
// End ProtoLog
java_library {
@@ -123,11 +124,12 @@ android_library {
"res",
],
java_resources: [
- ":generate-wm_shell_protolog.json"
+ ":generate-wm_shell_protolog.json",
],
static_libs: [
"androidx.appcompat_appcompat",
"androidx.arch.core_core-runtime",
+ "androidx-constraintlayout_constraintlayout",
"androidx.dynamicanimation_dynamicanimation",
"androidx.recyclerview_recyclerview",
"kotlinx-coroutines-android",
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml
deleted file mode 100644
index 29d9b257cc59..000000000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:propertyName="alpha"
- android:valueTo="1"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100" />
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml
deleted file mode 100644
index 70f553b89657..000000000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:propertyName="alpha"
- android:valueTo="0"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100" />
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml
deleted file mode 100644
index 29d9b257cc59..000000000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:propertyName="alpha"
- android:valueTo="1"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100" />
diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml
deleted file mode 100644
index 70f553b89657..000000000000
--- a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:propertyName="alpha"
- android:valueTo="0"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="100" />
diff --git a/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml b/libs/WindowManager/Shell/res/color/compat_background_ripple.xml
index 329e5b9b31a0..329e5b9b31a0 100644
--- a/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml
+++ b/libs/WindowManager/Shell/res/color/compat_background_ripple.xml
diff --git a/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml
new file mode 100644
index 000000000000..275870450493
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="@color/tv_pip_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" />
+</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_pip_menu_icon_bg.xml
new file mode 100644
index 000000000000..4f5e63dac5c0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/tv_pip_menu_icon_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<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" />
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml
new file mode 100644
index 000000000000..1c8cb914af81
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml
@@ -0,0 +1,33 @@
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <group>
+ <clip-path
+ android:pathData="M48,43l-48,-0l-0,-43l48,-0z"/>
+ <path
+ android:pathData="M24,43C37.2548,43 48,32.2548 48,19L48,0L0,-0L0,19C0,32.2548 10.7452,43 24,43Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M31,12.41L29.59,11L24,16.59L18.41,11L17,12.41L22.59,18L17,23.59L18.41,25L24,19.41L29.59,25L31,23.59L25.41,18L31,12.41Z"
+ android:fillColor="@color/compat_controls_text"/>
+ </group>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml
new file mode 100644
index 000000000000..c81013966c35
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_dismiss_button"/>
+</ripple> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml
new file mode 100644
index 000000000000..c796b5967f98
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <path
+ android:pathData="M24,0C10.7452,0 0,10.7452 0,24V43H48V24C48,10.7452 37.2548,0 24,0Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M32,17H28.83L27,15H21L19.17,17H16C14.9,17 14,17.9 14,19V31C14,32.1 14.9,33 16,33H32C33.1,33 34,32.1 34,31V19C34,17.9 33.1,17 32,17ZM32,31H16V19H32V31Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M24.6618,22C23.0436,22 21.578,22.6187 20.4483,23.625L18.25,21.375V27H23.7458L21.5353,24.7375C22.3841,24.0125 23.4649,23.5625 24.6618,23.5625C26.8235,23.5625 28.6616,25.0062 29.3028,27L30.75,26.5125C29.9012,23.8938 27.5013,22 24.6618,22Z"
+ android:fillColor="@color/compat_controls_text"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml
new file mode 100644
index 000000000000..3e9fe6dc3b99
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_treatment_applied_button"/>
+</ripple>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml
new file mode 100644
index 000000000000..af505d1cb73c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml
@@ -0,0 +1,53 @@
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <path
+ android:pathData="M24,0C10.7452,0 0,10.7452 0,24V43H48V24C48,10.7452 37.2548,0 24,0Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M32,17H28.83L27,15H21L19.17,17H16C14.9,17 14,17.9 14,19V31C14,32.1 14.9,33 16,33H32C33.1,33 34,32.1 34,31V19C34,17.9 33.1,17 32,17ZM32,31H16V19H32V31Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,29L18,25.5L19.5,25.5L19.5,29L18,29Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,29L30,25.5L28.5,25.5L28.5,29L30,29Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,21L30,24.5L28.5,24.5L28.5,21L30,21Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,21L18,24.5L19.5,24.5L19.5,21L18,21Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,27.5L21.5,27.5L21.5,29L18,29L18,27.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,27.5L26.5,27.5L26.5,29L30,29L30,27.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,22.5L26.5,22.5L26.5,21L30,21L30,22.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,22.5L21.5,22.5L21.5,21L18,21L18,22.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml
new file mode 100644
index 000000000000..c0f1c89b0cbb
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_treatment_suggested_button"/>
+</ripple>
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dialog_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dialog_background.xml
new file mode 100644
index 000000000000..3e1a2bce2393
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_dialog_background.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">
+ <solid android:color="@color/compat_controls_background"/>
+ <corners android:radius="@dimen/letterbox_education_dialog_corner_radius"/>
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background.xml
new file mode 100644
index 000000000000..0d8811357c05
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background.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">
+ <solid android:color="@color/letterbox_education_accent_primary"/>
+ <corners android:radius="12dp"/>
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml
new file mode 100644
index 000000000000..0d8a8faa9798
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_dismiss_button_background_ripple.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@android:color/system_accent1_10">
+ <item android:drawable="@drawable/letterbox_education_dismiss_button_background"/>
+</ripple> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_letterboxed_app.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_letterboxed_app.xml
new file mode 100644
index 000000000000..6fcd1de892a3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_letterboxed_app.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/letterbox_education_dialog_icon_size"
+ android:height="@dimen/letterbox_education_dialog_icon_size"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:fillColor="@color/letterbox_education_accent_primary"
+ android:fillType="evenOdd"
+ android:pathData="M2 8C0.895431 8 0 8.89543 0 10V38C0 39.1046 0.895431 40 2 40H46C47.1046 40 48 39.1046 48 38V10C48 8.89543 47.1046 8 46 8H2ZM44 12H4V36H44V12Z" />
+ <path
+ android:fillColor="@color/letterbox_education_accent_primary"
+ android:pathData="M 17 14 L 31 14 Q 32 14 32 15 L 32 33 Q 32 34 31 34 L 17 34 Q 16 34 16 33 L 16 15 Q 16 14 17 14 Z" />
+</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml
new file mode 100644
index 000000000000..cbfcfd06e3b7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_reposition.xml
@@ -0,0 +1,32 @@
+<?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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/letterbox_education_dialog_icon_size"
+ android:height="@dimen/letterbox_education_dialog_icon_size"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:fillColor="@color/letterbox_education_text_secondary"
+ android:fillType="evenOdd"
+ android:pathData="M2 8C0.895431 8 0 8.89543 0 10V38C0 39.1046 0.895431 40 2 40H46C47.1046 40 48 39.1046 48 38V10C48 8.89543 47.1046 8 46 8H2ZM44 12H4V36H44V12Z" />
+ <path
+ android:fillColor="@color/letterbox_education_text_secondary"
+ android:pathData="M 14 22 H 30 V 26 H 14 V 22 Z" />
+ <path
+ android:fillColor="@color/letterbox_education_text_secondary"
+ android:pathData="M26 16L34 24L26 32V16Z" />
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_screen_rotation.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_screen_rotation.xml
new file mode 100644
index 000000000000..469eb1e14849
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_screen_rotation.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/letterbox_education_dialog_icon_size"
+ android:height="@dimen/letterbox_education_dialog_icon_size"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:fillColor="@color/letterbox_education_text_secondary"
+ android:fillType="evenOdd"
+ android:pathData="M22.56 2H26C37.02 2 46 10.98 46 22H42C42 14.44 36.74 8.1 29.7 6.42L31.74 10L28.26 12L22.56 2ZM22 46H25.44L19.74 36L16.26 38L18.3 41.58C11.26 39.9 6 33.56 6 26H2C2 37.02 10.98 46 22 46ZM20.46 12L36 27.52L27.54 36L12 20.48L20.46 12ZM17.64 9.18C18.42 8.4 19.44 8 20.46 8C21.5 8 22.52 8.4 23.3 9.16L38.84 24.7C40.4 26.26 40.4 28.78 38.84 30.34L30.36 38.82C29.58 39.6 28.56 40 27.54 40C26.52 40 25.5 39.6 24.72 38.82L9.18 23.28C7.62 21.72 7.62 19.2 9.18 17.64L17.64 9.18Z" />
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml
new file mode 100644
index 000000000000..dcb8aed05c9c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_split_screen.xml
@@ -0,0 +1,32 @@
+<?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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/letterbox_education_dialog_icon_size"
+ android:height="@dimen/letterbox_education_dialog_icon_size"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+ <path
+ android:fillColor="@color/letterbox_education_text_secondary"
+ android:fillType="evenOdd"
+ android:pathData="M2 8C0.895431 8 0 8.89543 0 10V38C0 39.1046 0.895431 40 2 40H46C47.1046 40 48 39.1046 48 38V10C48 8.89543 47.1046 8 46 8H2ZM44 12H4V36H44V12Z" />
+ <path
+ android:fillColor="@color/letterbox_education_text_secondary"
+ android:pathData="M6 16C6 14.8954 6.89543 14 8 14H21C22.1046 14 23 14.8954 23 16V32C23 33.1046 22.1046 34 21 34H8C6.89543 34 6 33.1046 6 32V16Z" />
+ <path
+ android:fillColor="@color/letterbox_education_text_secondary"
+ android:pathData="M25 16C25 14.8954 25.8954 14 27 14H40C41.1046 14 42 14.8954 42 16V32C42 33.1046 41.1046 34 40 34H27C25.8954 34 25 33.1046 25 32V16Z" />
+</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml
new file mode 100644
index 000000000000..d8f356164358
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@color/tv_pip_menu_focus_border"
+ android:pathData="M7,10l5,5 5,-5H7z"/>
+</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml
new file mode 100644
index 000000000000..3e0011c65942
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@color/tv_pip_menu_focus_border"
+ android:pathData="M14,7l-5,5 5,5V7z"/>
+</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml
new file mode 100644
index 000000000000..f6b3c72e3cb5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@color/tv_pip_menu_focus_border"
+ android:pathData="M10,17l5,-5 -5,-5v10z"/>
+</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml
new file mode 100644
index 000000000000..1a3446249573
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@color/tv_pip_menu_focus_border"
+ android:pathData="M7,14l5,-5 5,5H7z"/>
+</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml
new file mode 100644
index 000000000000..37f4c87006ba
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_move_white.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<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="M11,5.83L11,10h2L13,5.83l1.83,1.83 1.41,-1.42L12,2 7.76,6.24l1.41,1.42zM17.76,7.76l-1.42,1.41L18.17,11L14,11v2h4.17l-1.83,1.83 1.42,1.41L22,12zM13,18.17L13,14h-2v4.17l-1.83,-1.83 -1.41,1.42L12,22l4.24,-4.24 -1.41,-1.42zM10,13v-2L5.83,11l1.83,-1.83 -1.42,-1.41L2,12l4.24,4.24 1.42,-1.41L5.83,13z"
+ android:fillColor="#FFFFFF" />
+
+</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index ab74e43472c3..e6ae28207970 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -21,7 +21,9 @@
android:viewportHeight="48">
<path
android:fillColor="@color/compat_controls_background"
- android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"
+ android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0"/>
<group
android:translateX="12"
android:translateY="12">
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
index 95decff24ac4..6551edf6d0e6 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
@@ -15,6 +15,6 @@
~ limitations under the License.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/size_compat_background_ripple">
+ android:color="@color/compat_background_ripple">
<item android:drawable="@drawable/size_compat_restart_button"/>
</ripple> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
index cce13035dba7..1938f4562e97 100644
--- a/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml
+++ b/libs/WindowManager/Shell/res/drawable/tv_pip_button_bg.xml
@@ -14,5 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="#9AFFFFFF" android:radius="17dp" />
+<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
new file mode 100644
index 000000000000..9bc03112b118
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/pip_menu_border_radius" />
+ <stroke android:width="@dimen/pip_menu_border_width"
+ android:color="@color/tv_pip_menu_focus_border" />
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/background_panel.xml b/libs/WindowManager/Shell/res/layout/background_panel.xml
new file mode 100644
index 000000000000..c3569d80fa1e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/background_panel.xml
@@ -0,0 +1,26 @@
+<?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
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/background_panel_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center_horizontal | center_vertical"
+ android:background="@android:color/transparent">
+</LinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/badged_image_view.xml b/libs/WindowManager/Shell/res/layout/badged_image_view.xml
new file mode 100644
index 000000000000..5f07121ec7d3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/badged_image_view.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <ImageView
+ android:id="@+id/icon_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:contentDescription="@null" />
+
+ <!--
+ Icon badge size is defined in Launcher3 BaseIconFactory as 0.444 of icon size.
+ Constraint guide starts from left, which means for a badge positioned on the right,
+ percent has to be 1 - 0.444 to have the same effect.
+ -->
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/app_icon_constraint_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.556" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/app_icon_constraint_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_percent="0.556" />
+
+ <ImageView
+ android:id="@+id/app_icon_view"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:contentDescription="@null"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="@id/app_icon_constraint_vertical"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="@id/app_icon_constraint_horizontal" />
+
+</merge> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
index bb48bf7b8b2c..44b2f45052ba 100644
--- a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
@@ -16,7 +16,7 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:clipToPadding="false"
@@ -26,7 +26,7 @@
<TextView
android:id="@+id/compat_mode_hint_text"
- android:layout_width="188dp"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="4sp"
android:background="@drawable/compat_hint_bubble"
diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index dc1683475c48..dfaeeeb81c07 100644
--- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -21,11 +21,47 @@
android:orientation="vertical"
android:gravity="bottom|end">
+ <include android:id="@+id/camera_compat_hint"
+ android:visibility="gone"
+ android:layout_width="@dimen/camera_compat_hint_width"
+ android:layout_height="wrap_content"
+ layout="@layout/compat_mode_hint"/>
+
+ <LinearLayout
+ android:id="@+id/camera_compat_control"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:layout_marginEnd="@dimen/compat_button_margin"
+ android:layout_marginBottom="@dimen/compat_button_margin"
+ android:orientation="vertical">
+
+ <ImageButton
+ android:id="@+id/camera_compat_treatment_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"/>
+
+ <ImageButton
+ android:id="@+id/camera_compat_dismiss_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/camera_compat_dismiss_ripple"
+ android:background="@android:color/transparent"
+ android:contentDescription="@string/camera_compat_dismiss_button_description"/>
+
+ </LinearLayout>
+
<include android:id="@+id/size_compat_hint"
- layout="@layout/compat_mode_hint"/>
+ android:visibility="gone"
+ android:layout_width="@dimen/size_compat_hint_width"
+ android:layout_height="wrap_content"
+ layout="@layout/compat_mode_hint"/>
<ImageButton
android:id="@+id/size_compat_restart_button"
+ android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/compat_button_margin"
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml
new file mode 100644
index 000000000000..cd1d99ae58b0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_action_layout.xml
@@ -0,0 +1,40 @@
+<!--
+ ~ 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/letterbox_education_dialog_action_width"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/letterbox_education_dialog_action_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginBottom="12dp"/>
+
+ <TextView
+ android:id="@+id/letterbox_education_dialog_action_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:lineSpacingExtra="4sp"
+ android:textAlignment="center"
+ android:textColor="@color/compat_controls_text"
+ android:textSize="14sp"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
new file mode 100644
index 000000000000..fc6ea9a57e10
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml
@@ -0,0 +1,99 @@
+<!--
+ ~ 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.
+ -->
+<com.android.wm.shell.compatui.letterboxedu.LetterboxEduDialogLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/system_neutral1_900">
+
+ <!-- The background of the top-level layout acts as the background dim. -->
+
+ <LinearLayout
+ android:id="@+id/letterbox_education_dialog_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:background="@drawable/letterbox_education_dialog_background"
+ android:padding="24dp">
+
+ <ImageView
+ android:id="@+id/letterbox_education_icon"
+ android:layout_width="@dimen/letterbox_education_dialog_icon_size"
+ android:layout_height="@dimen/letterbox_education_dialog_icon_size"
+ android:layout_marginBottom="12dp"
+ android:src="@drawable/letterbox_education_ic_letterboxed_app"/>
+
+ <TextView
+ android:id="@+id/letterbox_education_dialog_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="@dimen/letterbox_education_dialog_title_max_width"
+ android:lineSpacingExtra="4sp"
+ android:text="@string/letterbox_education_dialog_title"
+ android:textAlignment="center"
+ android:textColor="@color/compat_controls_text"
+ android:textSize="24sp"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:maxWidth="@dimen/letterbox_education_dialog_title_max_width"
+ android:lineSpacingExtra="4sp"
+ android:text="@string/letterbox_education_dialog_subtext"
+ android:textAlignment="center"
+ android:textColor="@color/letterbox_education_text_secondary"
+ android:textSize="14sp"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="top"
+ android:orientation="horizontal"
+ android:paddingTop="48dp">
+
+ <com.android.wm.shell.compatui.letterboxedu.LetterboxEduDialogActionLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:icon="@drawable/letterbox_education_ic_screen_rotation"
+ app:text="@string/letterbox_education_screen_rotation_text"/>
+
+ <com.android.wm.shell.compatui.letterboxedu.LetterboxEduDialogActionLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/letterbox_education_dialog_space_between_actions"
+ app:icon="@drawable/letterbox_education_ic_reposition"
+ app:text="@string/letterbox_education_reposition_text"/>
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/letterbox_education_dialog_dismiss_button"
+ android:layout_width="match_parent"
+ android:layout_height="56dp"
+ android:layout_marginTop="48dp"
+ android:background="@drawable/letterbox_education_dismiss_button_background_ripple"
+ android:text="@string/letterbox_education_got_it"
+ android:textColor="@android:color/system_neutral1_900"
+ android:textAlignment="center"
+ android:contentDescription="@string/letterbox_education_got_it"/>
+
+ </LinearLayout>
+
+</com.android.wm.shell.compatui.letterboxedu.LetterboxEduDialogLayout>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index 49e2379589a4..b56b114e9a4c 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -15,38 +15,101 @@
limitations under the License.
-->
<!-- Layout for TvPipMenuView -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<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:background="#CC000000">
-
- <LinearLayout
- android:id="@+id/tv_pip_menu_action_buttons"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="350dp"
- android:orientation="horizontal"
- android:alpha="0">
-
- <com.android.wm.shell.pip.tv.TvPipMenuActionButton
- android:id="@+id/tv_pip_menu_fullscreen_button"
- android:layout_width="@dimen/picture_in_picture_button_width"
- 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="@dimen/picture_in_picture_button_width"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
- android:src="@drawable/pip_ic_close_white"
- android:text="@string/pip_close" />
-
- <!-- More TvPipMenuActionButtons may be added here at runtime. -->
-
- </LinearLayout>
-
-</FrameLayout>
+ android:layout_height="match_parent">
+
+ <HorizontalScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_centerHorizontal="true"
+ android:gravity="center"
+ android:scrollbars="none"
+ android:layout_centerInParent="true"
+ android:layout_margin="@dimen/pip_menu_outer_space">
+
+ <LinearLayout
+ android:id="@+id/tv_pip_menu_action_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/pip_menu_button_wrapper_margin"
+ android:paddingEnd="@dimen/pip_menu_button_wrapper_margin"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:alpha="0">
+
+ <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_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_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. -->
+
+ </LinearLayout>
+ </HorizontalScrollView>
+
+ <View
+ android:id="@+id/tv_pip_menu_frame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:alpha="0"
+ android:layout_margin="@dimen/pip_menu_outer_space_frame"
+ android:background="@drawable/tv_pip_menu_border"/>
+
+ <ImageView
+ android:id="@+id/tv_pip_menu_arrow_up"
+ android:layout_width="@dimen/pip_menu_arrow_size"
+ android:layout_height="@dimen/pip_menu_arrow_size"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true"
+ android:alpha="0"
+ android:elevation="@dimen/pip_menu_arrow_elevation"
+ android:src="@drawable/pip_ic_move_up" />
+
+ <ImageView
+ android:id="@+id/tv_pip_menu_arrow_right"
+ android:layout_width="@dimen/pip_menu_arrow_size"
+ android:layout_height="@dimen/pip_menu_arrow_size"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:alpha="0"
+ android:elevation="@dimen/pip_menu_arrow_elevation"
+ android:src="@drawable/pip_ic_move_right" />
+
+ <ImageView
+ android:id="@+id/tv_pip_menu_arrow_down"
+ android:layout_width="@dimen/pip_menu_arrow_size"
+ android:layout_height="@dimen/pip_menu_arrow_size"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true"
+ android:alpha="0"
+ android:elevation="@dimen/pip_menu_arrow_elevation"
+ android:src="@drawable/pip_ic_move_down" />
+
+ <ImageView
+ android:id="@+id/tv_pip_menu_arrow_left"
+ android:layout_width="@dimen/pip_menu_arrow_size"
+ android:layout_height="@dimen/pip_menu_arrow_size"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:alpha="0"
+ 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
index 5925008e0d08..f9d0968a16df 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
@@ -15,36 +15,20 @@
limitations under the License.
-->
<!-- Layout for TvPipMenuActionButton -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<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:layout_marginStart="@dimen/pip_menu_button_margin"
+ android:layout_marginEnd="@dimen/pip_menu_button_margin"
+ android:background="@drawable/tv_pip_button_bg"
+ android:focusable="true">
- <ImageView android:id="@+id/button"
- android:layout_width="34dp"
- android:layout_height="34dp"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:focusable="true"
- android:src="@drawable/tv_pip_button_focused"
- android:importantForAccessibility="yes" />
-
- <ImageView android:id="@+id/icon"
- android:layout_width="34dp"
- android:layout_height="34dp"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:padding="5dp"
- android:importantForAccessibility="no" />
-
- <TextView android:id="@+id/desc"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:layout_below="@id/icon"
- android:layout_centerHorizontal="true"
- android:layout_marginTop="3dp"
- android:gravity="center"
- android:text="@string/pip_fullscreen"
- android:alpha="0"
- android:fontFamily="sans-serif"
- android:textSize="12sp"
- android:textColor="#EEEEEE"
- android:importantForAccessibility="no" />
-</merge>
+ <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_pip_menu_additional_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml
deleted file mode 100644
index bf4eb2691ff0..000000000000
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.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.
--->
-<com.android.wm.shell.pip.tv.TvPipMenuActionButton
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/picture_in_picture_button_width"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" />
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index c3ae053d156d..e45e409875c3 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Maak toe"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Vou uit"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Slaan oor na volgende"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Slaan oor na vorige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Verander grootte"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Hou vas"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Program sal dalk nie op \'n sekondêre skerm werk nie."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Het dit"</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="3924745395335329810">"Kry die meeste uit <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Draai jou skerm na portret"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Draai jou skerm na landskap"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Sleep nog \'n program in om verdeelde skerm te gebruik"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Dubbeltik om te herposisioneer"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Het dit"</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 6ce588034f9e..1bfe128b0917 100644
--- a/libs/WindowManager/Shell/res/values-af/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Titellose program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Maak PIP toe"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Volskerm"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Skuif PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index c889039cffdb..3a1f619fc7bf 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ዝጋ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ዘርጋ"</string>
<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_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> ይህን ባህሪ እንዲጠቀም ካልፈለጉ ቅንብሮችን ለመክፈት መታ ያድርጉና ያጥፉት።"</string>
@@ -28,6 +29,8 @@
<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_stash" msgid="4060775037619702641">"Stash"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"መተግበሪያ በሁለተኛ ማሳያ ላይ ላይሠራ ይችላል።"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"ገባኝ"</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="3924745395335329810">"ከ<xliff:g id="APP_NAME">%s</xliff:g> ማግኘት የሚችሉትን ያግኙ"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"የቁመት አቀማመጥ ለማድረግ ማያ ገጽዎን ያሽከርክሩት"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"የአግድም አቀማመጥ ለማድረግ ማያ ገጽዎን ያሽከርክሩት"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"የተከፈለ ማያ ገጽ ለመጠቀም ሌላ መተግበሪያ ይጎትቱ"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"ቦታ ለመቀየር ሁለቴ መታ ያድርጉ"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ገባኝ"</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 fcb87c5682e3..456b4b83583a 100644
--- a/libs/WindowManager/Shell/res/values-am/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ርዕስ የሌለው ፕሮግራም)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIPን ዝጋ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ሙሉ ማያ ገጽ"</string>
+ <string name="pip_move" msgid="1544227837964635439">"ፒአይፒ ውሰድ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 2c89b1d4eb56..1b890f55a673 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"إغلاق"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"توسيع"</string>
<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_notification_message" msgid="8854051911700302620">"إذا كنت لا تريد أن يستخدم <xliff:g id="NAME">%s</xliff:g> هذه الميزة، فانقر لفتح الإعدادات، ثم أوقِف تفعيل هذه الميزة."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"حسنًا"</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="3924745395335329810">"الاستفادة إلى أقصى حدّ من <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"تدوير الشاشة للوضع العمودي"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"تدوير الشاشة إلى الوضع الأفقي"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"اسحب تطبيقًا آخر لاستخدام وضع تقسيم الشاشة"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"انقر مرتين لتغيير الموضع"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"حسنًا"</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 4eef29e2ed12..2546fe96d86a 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ليس هناك عنوان للبرنامج)"</string>
<string name="pip_close" msgid="9135220303720555525">"‏إغلاق PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ملء الشاشة"</string>
+ <string name="pip_move" msgid="1544227837964635439">"‏نقل نافذة داخل النافذة (PIP)"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 0a74ac6391ce..a700fbf66c18 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -19,35 +19,38 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pip_phone_close" msgid="5783752637260411309">"বন্ধ কৰক"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"বিস্তাৰ কৰক"</string>
- <string name="pip_phone_settings" msgid="5468987116750491918">"ছেটিংসমূহ"</string>
+ <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_notification_message" msgid="8854051911700302620">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g> সুবিধাটো ব্যৱহাৰ কৰিব নোখোজে, তেন্তে ছেটিংসমূহ খুলিবলৈ টিপক আৰু তালৈ গৈ ইয়াক অফ কৰক।"</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"আপুনি যদি <xliff:g id="NAME">%s</xliff:g> সুবিধাটো ব্যৱহাৰ কৰিব নোখোজে, তেন্তে ছেটিং খুলিবলৈ টিপক আৰু তালৈ গৈ ইয়াক অফ কৰক।"</string>
<string name="pip_play" msgid="3496151081459417097">"প্লে কৰক"</string>
<string name="pip_pause" msgid="690688849510295232">"পজ কৰক"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"পৰৱৰ্তী মিডিয়ালৈ যাওক"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"আগৰটো মিডিয়ালৈ যাওক"</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_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপ্‌টোৱে বিভাজিত স্ক্ৰীন সমৰ্থন নকৰে।"</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="accessibility_action_divider_left_full" msgid="1792313656305328536">"বাওঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"স্প্লিট স্ক্ৰীনৰ বিভাজক"</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_right_full" msgid="3408505054325944903">"সোঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
- <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"শীৰ্ষ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"সোঁফালৰ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"শীৰ্ষ স্ক্ৰীনখন সম্পূৰ্ণ স্ক্ৰীন কৰক"</string>
<string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"শীর্ষ স্ক্ৰীণখন ৭০% কৰক"</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="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>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"এখন হাতেৰে ব্যৱহাৰ কৰা ম\'ডটো আৰম্ভ কৰক"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"এখন হাতেৰে ব্যৱহাৰ কৰা ম\'ডটোৰ পৰা বাহিৰ হওক"</string>
- <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ bubblesৰ ছেটিংসমূহ"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ bubblesৰ ছেটিং"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"অভাৰফ্ল’"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"ষ্টেকত পুনৰ যোগ দিয়ক"</string>
<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>
@@ -56,7 +59,7 @@
<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="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিংসমূহ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিং"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল অগ্ৰাহ্য কৰক"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"বাৰ্তালাপ বাবল নকৰিব"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles ব্যৱহাৰ কৰি চাট কৰক"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"বুজি পালোঁ"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g>পাৰ্যমানে ব্যৱহাৰ কৰক"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"প’ৰ্ট্ৰেইট কৰিবলৈ স্ক্ৰীনখন ঘূৰাওক"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"লেণ্ডস্কেইপ কৰিবলৈ স্ক্ৰীনখন ঘূৰাওক"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ অন্য এটা এপ্‌ ইয়ালৈ টানি আনি এৰক"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"স্থান সলনি কৰিবলৈ দুবাৰ টিপক"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"বুজি পালোঁ"</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 6c223f45d9b3..d17c1f3023a3 100644
--- a/libs/WindowManager/Shell/res/values-as/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -20,5 +20,6 @@
<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_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীণ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীন"</string>
+ <string name="pip_move" msgid="1544227837964635439">"পিপ স্থানান্তৰ কৰক"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 54483bfa730c..0dba9340d76d 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Bağlayın"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Genişləndirin"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Növbətiyə keçin"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Əvvəlkinə keçin"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ölçüsünü dəyişin"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Güvənli məkanda saxlayın"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Tətbiq ikinci ekranda işləməyə bilər."</string>
@@ -43,10 +46,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yuxarı 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Yuxarı 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Aşağı tam ekran"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bir əlli rejimdən istifadə edilir"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Birəlli rejim istifadəsi"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Çıxmaq üçün ekranın aşağısından yuxarıya doğru sürüşdürün və ya tətbiqin yuxarısında istənilən yerə toxunun"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bir əlli rejimi başladın"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bir əlli rejimdən çıxın"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Birəlli rejim başlasın"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Birəlli rejimdən çıxın"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> yumrucuqları üçün ayarlar"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Kənara çıxma"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Yenidən dəstəyə əlavə edin"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Anladım"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> tətbiqindən maksimum faydalanın"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Ekranı fırladaraq portret rejiminə keçin"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Ekranı fırladaraq albom rejiminə keçin"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Bölünmüş ekrandan istifadə etmək üçün başqa tətbiqə sürüşdürün"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Yerini dəyişdirmək üçün iki dəfə toxunun"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</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 c9f1acbef31b..a5c47924e31a 100644
--- a/libs/WindowManager/Shell/res/values-az/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP tətbiq edin"</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 5ed79c4901cd..afe411b7f8b5 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zatvori"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Proširi"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pređi na sledeće"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pređi na prethodno"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promenite veličinu"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavite u tajnu memoriju"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Važi"</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="3924745395335329810">"Iskoristite aplikaciju <xliff:g id="APP_NAME">%s</xliff:g> na najbolji način"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Rotirajte ekran u uspravni položaj"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Rotirajte ekran u vodoravni položaj"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Prevucite drugu aplikaciju da biste koristili podeljeni ekran"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Dvaput dodirnite radi premeštanja"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Važi"</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 6fbc91bbec60..b4d9bd17b5fe 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
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
<string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ceo ekran"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Premesti sliku u slici"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index a9a62de6c8f9..ef692c24f20c 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Закрыць"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Разгарнуць"</string>
<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_notification_message" msgid="8854051911700302620">"Калі вы не хочаце, каб праграма <xliff:g id="NAME">%s</xliff:g> выкарыстоўвала гэту функцыю, дакраніцеся, каб адкрыць налады і адключыць яе."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Праграма можа не працаваць на дадатковых экранах."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Зразумела"</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="3924745395335329810">"Выкарыстоўвайце ўсе магчымасці праграмы \"<xliff:g id="APP_NAME">%s</xliff:g>\""</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Павярніце экран у кніжную арыентацыю"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Павярніце экран у альбомную арыентацыю"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Перацягніце іншую праграму, каб выкарыстоўваць падзелены экран"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Двойчы націсніце, каб перасунуць"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Зразумела"</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 d33bf99e2ebd..514d06b5b179 100644
--- a/libs/WindowManager/Shell/res/values-be/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Праграма без назвы)"</string>
<string name="pip_close" msgid="9135220303720555525">"Закрыць PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Поўнаэкранны рэжым"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Перамясціць PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 80895dc0c032..4ca46b585d1c 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Затваряне"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Разгъване"</string>
<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_notification_message" msgid="8854051911700302620">"Ако не искате <xliff:g id="NAME">%s</xliff:g> да използва тази функция, докоснете, за да отворите настройките, и я изключете."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Възможно е приложението да не работи на алтернативни дисплеи."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Разбрах"</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="3924745395335329810">"Използване на <xliff:g id="APP_NAME">%s</xliff:g> в пълна степен"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Завъртете екрана си вертикално"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Завъртете екрана си хоризонтално"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Преместете друго приложение с плъзгане, за да използвате режима за разделен екран"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Докоснете два пъти за промяна на позицията"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Разбрах"</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 f4fad601179f..19f83e71ea44 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без заглавие)"</string>
<string name="pip_close" msgid="9135220303720555525">"Затваряне на PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Цял екран"</string>
+ <string name="pip_move" msgid="1544227837964635439">"„Картина в картина“: Преместв."</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index bdda799b001f..fa62bbab1daa 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"বন্ধ করুন"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"বড় করুন"</string>
<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_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> কে এই বৈশিষ্ট্যটি ব্যবহার করতে দিতে না চাইলে ট্যাপ করে সেটিংসে গিয়ে সেটি বন্ধ করে দিন।"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"বুঝেছি"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g>-এর সবচেয়ে বেশি সুবিধা নিন"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"আপনার স্ক্রিন ঘুরিয়ে পোর্ট্রেট করুন"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"আপনার স্ক্রিন ঘুরিয়ে ল্যান্ডস্কেপ করুন"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"স্প্লিট স্ক্রিন ফিচার ব্যবহার করতে অন্য অ্যাপে টেনে আনুন"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"জায়গা পরিবর্তন করতে ডবল ট্যাপ করুন"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"বুঝেছি"</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 0eb83a0276e6..5f90eeb35a3f 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(শিরোনামহীন প্রোগ্রাম)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP বন্ধ করুন"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"পূর্ণ স্ক্রিন"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP সরান"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 759e9b8c415b..043a30975c2f 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zatvori"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Proširi"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeći"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodni"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavljanje u stash"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Razumijem"</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="3924745395335329810">"Iskoristite sve prednosti aplikacije <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Zarotirajte ekran u uspravni položaj"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Zarotirajte ekran u vodoravni položaj"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Prevucite još jednu aplikaciju da koristite podijeljeni ekran"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Dodirnite dvaput da premjestite"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Razumijem"</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 8e301b0a8f4d..3f2adf3600d7 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
@@ -19,6 +19,7 @@
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="9135220303720555525">"Zatvori sliku u slici"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli ekran"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Pokreni sliku u slici"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 202ea2083b7e..3ee8a2329453 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Tanca"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Desplega"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ves al següent"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Torna a l\'anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Canvia la mida"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Amaga"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Entesos"</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="3924745395335329810">"Treu el màxim profit de l\'aplicació <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Gira la pantalla per posar-la en vertical"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Gira la pantalla per posar-la en horitzontal"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Arrossega una altra aplicació per utilitzar la pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Fes doble toc per canviar la posició"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entesos"</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 b80fc41402dd..db750c49884e 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Mou pantalla en pantalla"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 08a4201b2fae..00e5827b5804 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zavřít"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Rozbalit"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Přeskočit na další"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Přeskočit na předchozí"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Změnit velikost"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Uložit"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikace na sekundárním displeji nemusí fungovat."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Rozumím"</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="3924745395335329810">"Využijte <xliff:g id="APP_NAME">%s</xliff:g> na maximum"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Otočením obrazovky přejdete do zobrazení na výšku"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Otočením obrazovky přejdete do zobrazení na šířku"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Přetáhnutím druhé aplikace použijete rozdělenou obrazovku"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Dvojitým klepnutím změníte umístění"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 56abcbe473fb..cef0b9951363 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Přesunout PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 395f6e75cdbc..bdcb6d88d090 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Luk"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Udvid"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Gå videre til næste"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Gå til forrige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Rediger størrelse"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Skjul"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer muligvis ikke på sekundære skærme."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Få mest muligt ud af <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Roter din skærm til stående format"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Roter din skærm til liggende format"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Træk en anden app hertil for at bruge opdelt skærm"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Tryk to gange for at flytte"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 fdb6b783399e..23305309098d 100644
--- a/libs/WindowManager/Shell/res/values-da/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Fuld skærm"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Flyt PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index ab3461a1ebf5..472e94134fe3 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Schließen"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Maximieren"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Einstellungen"</string>
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"„Bildschirm teilen“ aktivieren"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menü"</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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Vorwärts springen"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Rückwärts springen"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Größe anpassen"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"In Stash legen"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Aus Stash entfernen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Die App funktioniert unter Umständen bei geteiltem Bildschirmmodus 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="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
@@ -60,9 +63,9 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles zum Chatten verwenden"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, \"Bubbles\" genannt. Wenn du die Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, „Bubbles“ genannt. Wenn du eine Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bubble-Einstellungen festlegen"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf \"Verwalten\", um Bubbles für diese App zu deaktivieren"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf „Verwalten“, um Bubbles für diese App zu deaktivieren"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Ok"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> optimal nutzen"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Display ins Hochformat drehen"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Display ins Querformat drehen"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Weitere App hierher ziehen, um den Bildschirm zu teilen"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Doppeltippen, um Position anzupassen"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ok"</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 02cce9d73647..8da9110f5e03 100644
--- a/libs/WindowManager/Shell/res/values-de/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Vollbild"</string>
+ <string name="pip_move" msgid="1544227837964635439">"BiB verschieben"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 75e4379284b8..53edf1eea1a6 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Κλείσιμο"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Ανάπτυξη"</string>
<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">"Η λειτουργία 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>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Η εφαρμογή ίσως να μην λειτουργήσει σε δευτερεύουσα οθόνη."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Το κατάλαβα"</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="3924745395335329810">"Αξιοποιήστε όλες τις δυνατότητες του <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Περιστροφή της οθόνης σε προσανατολισμό πορτρέτου"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Περιστροφή της οθόνης σε οριζόντιο προσανατολισμό"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Σύρετε σε μια άλλη εφαρμογή για να χρησιμοποιήσετε τον διαχωρισμό οθόνης"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Διπλό πάτημα για επανατοποθέτηση"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Το κατάλαβα"</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 880ea37e6bf7..df35113a8752 100644
--- a/libs/WindowManager/Shell/res/values-el/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Δεν υπάρχει τίτλος προγράμματος)"</string>
<string name="pip_close" msgid="9135220303720555525">"Κλείσιμο PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Πλήρης οθόνη"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Μετακίνηση PIP"</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 0d7b60ffefc6..eb806a7d94e4 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="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="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Get the most out of <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Rotate your screen to portrait"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Rotate your screen to landscape"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Drag in another app to use split screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Double tap to reposition"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</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 e3f08c8cc76f..1fb319196bef 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Move PIP"</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 0d7b60ffefc6..eb806a7d94e4 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="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="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Get the most out of <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Rotate your screen to portrait"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Rotate your screen to landscape"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Drag in another app to use split screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Double tap to reposition"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</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 e3f08c8cc76f..1fb319196bef 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Move PIP"</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 0d7b60ffefc6..eb806a7d94e4 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="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="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Get the most out of <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Rotate your screen to portrait"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Rotate your screen to landscape"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Drag in another app to use split screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Double tap to reposition"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</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 e3f08c8cc76f..1fb319196bef 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Move PIP"</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 0d7b60ffefc6..eb806a7d94e4 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="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="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Get the most out of <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Rotate your screen to portrait"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Rotate your screen to landscape"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Drag in another app to use split screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Double tap to reposition"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</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 e3f08c8cc76f..1fb319196bef 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(No title program)"</string>
<string name="pip_close" msgid="9135220303720555525">"Close PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Move PIP"</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 4bff89d68963..62123eb2db1b 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎Close‎‏‎‎‏‎"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎Expand‎‏‎‎‏‎"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎Skip to next‎‏‎‎‏‎"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎Skip to previous‎‏‎‎‏‎"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎Resize‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎Stash‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎Unstash‎‏‎‎‏‎"</string>
<string name="dock_forced_resizable" msgid="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="forced_resizable_secondary_display" msgid="1768046938673582671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎App may not work on a secondary display.‎‏‎‎‏‎"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‏‏‏‎Got it‎‏‎‎‏‎"</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="3924745395335329810">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎Get the most out of ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‎Rotate your screen to portrait‎‏‎‎‏‎"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‏‎Rotate your screen to landscape‎‏‎‎‏‎"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‎‏‎Drag in another app to use split screen‎‏‎‎‏‎"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎Double tap to reposition‎‏‎‎‏‎"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎‏‎Got it‎‏‎‎‏‎"</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 3f9ef0ea2816..3b12d90f33a2 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎(No title program)‎‏‎‎‏‎"</string>
<string name="pip_close" msgid="9135220303720555525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎Close PIP‎‏‎‎‏‎"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎Full screen‎‏‎‎‏‎"</string>
+ <string name="pip_move" msgid="1544227837964635439">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎Move PIP‎‏‎‎‏‎"</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 90c4d51b995a..6db097568551 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Cerrar"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Siguiente"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar el tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Almacenar de manera segura"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la app no funcione en una pantalla secundaria."</string>
@@ -58,7 +61,7 @@
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbujas"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbuja"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat con burbujas"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como elementos flotantes o burbujas. Presiona para abrir la burbuja. Arrástrala para moverla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Entendido"</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="3924745395335329810">"Aprovecha <xliff:g id="APP_NAME">%s</xliff:g> al máximo"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Rota la pantalla al modo vertical"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Rota la pantalla al modo horizontal"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Arrastra otra app para usar la pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Presiona dos veces para cambiar la posición"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</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 5d5954a19761..1beb0b5b6255 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Mover PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 2207e62e9503..4356dc8128af 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Cerrar"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Mostrar"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Saltar al siguiente"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Volver al anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Esconder"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
@@ -60,7 +63,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 para abrir la burbuja. Arrastra para moverla."</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_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>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Entendido"</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="3924745395335329810">"Saca el máximo partido de <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Gira la pantalla para ponerla en el modo de vista vertical"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Gira la pantalla para ponerla en el modo de vista horizontal"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Arrastra otra aplicación para usar la pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Toca dos veces para cambiar de posición"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</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 d31b9b45cae3..d042b43c8ce8 100644
--- a/libs/WindowManager/Shell/res/values-es/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Mover imagen en imagen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 9222a91b044c..3dba8cc5f524 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Sule"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Laiendamine"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Järgmise juurde"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Eelmise juurde"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Suuruse muutmine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Pane hoidlasse"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Selge"</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="3924745395335329810">"Kasutage <xliff:g id="APP_NAME">%s</xliff:g> võimalusi"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Pöörake ekraan vertikaalpaigutusse"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Pöörake ekraan horisontaalpaigutusse"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Lohistage muusse rakendusse, et jagatud ekraanikuva kasutada"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Topeltpuudutage ümberpaigutamiseks"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Selge"</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 bc7a6adafc03..3da16db9e196 100644
--- a/libs/WindowManager/Shell/res/values-et/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programmi pealkiri puudub)"</string>
<string name="pip_close" msgid="9135220303720555525">"Sule PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Täisekraan"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Teisalda PIP-režiimi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 4a59b59df27b..ab70b3accedb 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Itxi"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Zabaldu"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Joan hurrengora"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Joan aurrekora"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Aldatu tamaina"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Gorde"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Baliteke aplikazioak ez funtzionatzea bigarren mailako pantailetan."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Ados"</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="3924745395335329810">"Atera ahalik eta etekin handiena <xliff:g id="APP_NAME">%s</xliff:g> aplikazioari"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Edukia bertikalki ikusteko, biratu pantaila"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Edukia horizontalki ikusteko, biratu pantaila"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Pantaila zatituta ikusteko, arrastatu beste aplikazio bat"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Aplikazioaren posizioa aldatzeko, saka ezazu birritan"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ados"</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 cf5f98883082..e4b57baf1e5f 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa izengabea)"</string>
<string name="pip_close" msgid="9135220303720555525">"Itxi PIPa"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Pantaila osoa"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Mugitu pantaila txiki gainjarria"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index a17f543278f7..bef49d3febca 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"بستن"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"بزرگ کردن"</string>
<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_notification_message" msgid="8854051911700302620">"اگر نمی‌خواهید <xliff:g id="NAME">%s</xliff:g> از این قابلیت استفاده کند، با ضربه زدن، تنظیمات را باز کنید و آن را خاموش کنید."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن است برنامه در نمایشگر ثانویه کار نکند."</string>
@@ -43,7 +46,7 @@
<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_title" msgid="4583241688067426350">"استفاده از حالت یک‌دستی"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"برای خارج شدن، از پایین صفحه‌نمایش تند به‌طرف بالا بکشید یا در هر جایی از بالای برنامه که می‌خواهید ضربه بزنید"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"آغاز «حالت تک حرکت»"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"خروج از «حالت تک حرکت»"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"متوجه‌ام"</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="3924745395335329810">"بیشترین بهره را از <xliff:g id="APP_NAME">%s</xliff:g> ببرید"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"صفحه‌نمایش را در جهت عمودی بچرخانید"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"صفحه‌نمایش را در جهت افقی بچرخانید"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"برای استفاده از صفحهٔ دونیمه، در برنامه‌ای دیگر بکشید"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"برای تغییر مکان، دوضربه بزنید"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"متوجه‌ام"</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 5b815b4c7b86..aaab34f807db 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(برنامه بدون عنوان)"</string>
<string name="pip_close" msgid="9135220303720555525">"‏بستن PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"تمام صفحه"</string>
+ <string name="pip_move" msgid="1544227837964635439">"‏انتقال PIP (تصویر در تصویر)"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 332dc9b14da2..c5b9ece0fb87 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Sulje"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Laajenna"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Siirry seuraavaan"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Siirry edelliseen"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Muuta kokoa"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Lisää turvasäilytykseen"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> niin kuin se on tarkoitettu"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Vaihda pystysuuntaan kääntämällä näyttöä"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Vaihda vaakasuuntaan kääntämällä näyttöä"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Käytä jaettua näyttöä vetämällä tähän toinen sovellus"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Sijoita uudelleen kaksoisnapauttamalla"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 77ad6eef91e7..21c64633fac1 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nimetön)"</string>
<string name="pip_close" msgid="9135220303720555525">"Sulje PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Koko näyttö"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Siirrä PIP"</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 f51fc66b6b83..5689b5927f34 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Fermer"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Développer"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au suivant"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Revenir au précédent"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ajouter à la réserve"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Profitez de toutes les possibilités de <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Faites pivoter votre écran pour passer en mode portrait"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Faites pivoter votre écran pour passer en mode paysage"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Faites glisser une autre application pour utiliser l\'écran partagé"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Touchez deux fois pour repositionner"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 0ec7f40f0e9f..f4baaad13999 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Déplacer l\'image incrustée"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 8fa06e88cd50..670a24229e96 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Fermer"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Développer"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au contenu suivant"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Passer au contenu précédent"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="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="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
@@ -61,7 +64,7 @@
<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_manage_title" msgid="7042699946735628035">"Contrôler les paramètres des bulles"</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>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Tirez le meilleur parti de <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Faites pivoter votre écran en mode Portrait"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Faites pivoter votre écran en mode Paysage"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Faites glisser une autre application pour utiliser l\'écran partagé"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Appuyez deux fois pour repositionner"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 27fd155535b7..6ad8174db796 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Déplacer le PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 56188d4e92ba..05598389f4ab 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Pechar"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Despregar"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ir ao seguinte"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ir ao anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Esconder"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Entendido"</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="3924745395335329810">"Sácalle o máximo partido a <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Xira a pantalla para poñela en modo vertical"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Xira a pantalla para poñela en modo horizontal"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Arrastra outra aplicación para usar a pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Toca dúas veces para cambiar a posición"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</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 df96f6cb794d..dcb8709d010e 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Mover pantalla superposta"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index b76e91010dcc..ec3093a6892e 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"બંધ કરો"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"વિસ્તૃત કરો"</string>
<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_notification_message" msgid="8854051911700302620">"જો તમે નથી ઇચ્છતા કે <xliff:g id="NAME">%s</xliff:g> આ સુવિધાનો ઉપયોગ કરે, તો સેટિંગ ખોલવા માટે ટૅપ કરો અને તેને બંધ કરો."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર કદાચ કામ ન કરે."</string>
@@ -59,7 +62,7 @@
<string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> સેટિંગ"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"બબલને છોડી દો"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"વાતચીતને બબલ કરશો નહીં"</string>
- <string name="bubbles_user_education_title" msgid="2112319053732691899">"બબલનો ઉપયોગ કરીને ચેટ કરો"</string>
+ <string name="bubbles_user_education_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>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"સમજાઈ ગયું"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g>નો સૌથી વધુ લાભ મેળવો"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"પોર્ટ્રેટ સ્થિતિ માટે તમારી સ્ક્રીનને ફેરવો"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"લૅન્ડસ્કેપ સ્થિતિ માટે તમારી સ્ક્રીનને ફેરવો"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"સ્ક્રીન વિભાજનનો ઉપયોગ કરવા કોઈ અન્ય ઍપમાં ખેંચો"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"સ્થાનમાં ફેરફાર કરવા માટે બે વાર ટૅપ કરો"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"સમજાઈ ગયું"</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 3608f1d530c0..ed815caaed0f 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(કોઈ ટાઇટલ પ્રોગ્રામ નથી)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP બંધ કરો"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"પૂર્ણ સ્ક્રીન"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP ખસેડો"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index b2c005544c32..d1776d831180 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"बंद करें"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"विस्तार करें"</string>
<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_notification_message" msgid="8854051911700302620">"अगर आप नहीं चाहते कि <xliff:g id="NAME">%s</xliff:g> इस सुविधा का उपयोग करे, तो सेटिंग खोलने के लिए टैप करें और उसे बंद करें ."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"हो सकता है कि ऐप प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर काम न करे."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"ठीक है"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> का ज़्यादा से ज़्यादा फ़ायदा पाएं"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"अपनी स्क्रीन को घुमाकर, पोर्ट्रेट मोड में लाएं"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"अपनी स्क्रीन को घुमाकर, लैंडस्केप मोड में लाएं"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"स्प्लिट स्क्रीन का इस्तेमाल करने के लिए, दूसरे ऐप्लिकेशन को खींचें और छोड़ें"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"ऐप्लिकेशन की जगह बदलने के लिए, दो बार टैप करें"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ठीक है"</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 720bb6ca5e24..8bcc631b39a2 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(कोई शीर्षक कार्यक्रम नहीं)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP बंद करें"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फ़ुल स्‍क्रीन"</string>
+ <string name="pip_move" msgid="1544227837964635439">"पीआईपी को दूसरी जगह लेकर जाएं"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 769d1d26aed2..09860903cc8a 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zatvori"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Proširivanje"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeće"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodno"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Sakrijte"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Shvaćam"</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="3924745395335329810">"Iskoristite sve prednosti aplikacije <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Zakrenite zaslon u okomiti položaj"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Zakrenite zaslon u vodoravni položaj"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Povucite drugu aplikaciju da biste upotrebljavali podijeljeni zaslon"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Dvaput dodirnite za premještanje"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Shvaćam"</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 21f8cb63f470..49b7ae0d7681 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez naslova)"</string>
<string name="pip_close" msgid="9135220303720555525">"Zatvori PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Cijeli zaslon"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Premjesti PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 05655f1bb607..f4027fb88368 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Bezárás"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Kibontás"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ugrás a következőre"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ugrás az előzőre"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Átméretezés"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Félretevés"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Rendben"</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="3924745395335329810">"Használja ki a(z) <xliff:g id="APP_NAME">%s</xliff:g> minden előnyét"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Képernyő elforgatása álló módba"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Képernyő elforgatása fekvő módba"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Húzzon ide egy másik alkalmazást az osztott képernyő használatához"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Koppintson duplán az áthelyezéshez"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Értem"</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 0010086bb0b5..484db0cac067 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Teljes képernyő"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP áthelyezése"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 5f7495e63613..c93469592c88 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Փակել"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Ընդարձակել"</string>
<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_notification_message" msgid="8854051911700302620">"Եթե չեք ցանկանում, որ <xliff:g id="NAME">%s</xliff:g>-ն օգտագործի այս գործառույթը, հպեք՝ կարգավորումները բացելու և այն անջատելու համար։"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Հավելվածը կարող է չաշխատել լրացուցիչ էկրանի վրա"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Եղավ"</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="3924745395335329810">"Օգտվեք <xliff:g id="APP_NAME">%s</xliff:g>-ի բոլոր հնարավորություններից"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Պտտեք էկրանը ուղղաձիգ դիրքի"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Պտտեք էկրանը հորիզոնական դիրքի"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Քաշեք մյուս հավելվածի մեջ՝ էկրանի տրոհումն օգտագործելու համար"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Կրկնակի հպեք՝ դիրքը փոխելու համար"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Եղավ"</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 cb18762be48b..e447ffc0d964 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Առանց վերնագրի ծրագիր)"</string>
<string name="pip_close" msgid="9135220303720555525">"Փակել PIP-ն"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Լիէկրան"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Տեղափոխել PIP-ը"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 2cf50c0644f1..5976fe13e959 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Tutup"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Luaskan"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Lewati ke berikutnya"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Lewati ke sebelumnya"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah ukuran"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Oke"</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="3924745395335329810">"Optimalkan penggunaan <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Putar layar ke mode potret"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Putar layar ke mode lanskap"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Tarik aplikasi lain untuk menggunakan layar terpisah"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Ketuk dua kali untuk mengubah posisi"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Oke"</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 8f3a28764b00..b63170564734 100644
--- a/libs/WindowManager/Shell/res/values-in/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tanpa judul)"</string>
<string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Layar penuh"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Pindahkan PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 7a3b6a693e7a..ee9940ae77b4 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Loka"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Stækka"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Fara á næsta"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Fara á fyrra"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Breyta stærð"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Geymsla"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Ég skil"</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="3924745395335329810">"Nýttu <xliff:g id="APP_NAME">%s</xliff:g> sem best"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Snúðu skjánum í skammsnið"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Snúðu skjánum í langsnið"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Dragðu annað forrit inn til að nota skjáskiptingu"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Ýttu tvisvar til að breyta staðsetningu"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ég skil"</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 1f148d948a0e..119ecf088c20 100644
--- a/libs/WindowManager/Shell/res/values-is/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Allur skjárinn"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Færa innfellda mynd"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index b061d1f592c4..f12289e9d6e1 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Chiudi"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Espandi"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passa ai contenuti successivi"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Passa ai contenuti precedenti"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ridimensiona"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Accantona"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"L\'app potrebbe non funzionare su un display secondario."</string>
@@ -60,7 +63,7 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta utilizzando le bolle"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono visualizzate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono mostrate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlla le bolle quando vuoi"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tocca Gestisci per disattivare le bolle dall\'app"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Usa al meglio l\'app <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Ruota lo schermo in verticale"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Ruota lo schermo in orizzontale"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Trascina in un\'altra app per usare lo schermo diviso"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Tocca due volte per riposizionare"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 127454cf28bf..92f015c387a0 100644
--- a/libs/WindowManager/Shell/res/values-it/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma senza titolo)"</string>
<string name="pip_close" msgid="9135220303720555525">"Chiudi PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Schermo intero"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Sposta PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 4b4310256766..eb63a21977dc 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"סגירה"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"הרחבה"</string>
<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_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולהשבית את התכונה."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"הבנתי"</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="3924745395335329810">"להפיק את המרב מ-<xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"מסובבים את המסך כדי להציג לאורך"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"מסובבים את המסך כדי להציג לרוחב"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"גוררים אפליקציה אחרת כדי להשתמש במסך מפוצל"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"מקישים הקשה כפולה כדי למקם מחדש"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"הבנתי"</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 ef98a9c41cf2..d09b850d01d8 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
<string name="pip_close" msgid="9135220303720555525">"‏סגירת PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
+ <string name="pip_move" msgid="1544227837964635439">"‏העברת תמונה בתוך תמונה (PIP)"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index ab693d2e5045..c6399e321070 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"閉じる"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
<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_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g>でこの機能を使用しない場合は、タップして設定を開いて OFF にしてください。"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"アプリはセカンダリ ディスプレイでは動作しないことがあります。"</string>
@@ -61,7 +64,7 @@
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"チャットでバブルを使う"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"いつでもバブルを管理"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"バブルはいつでも管理可能"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"このアプリからのバブルを OFF にするには、[管理] をタップしてください"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近閉じたバブルはありません"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> の活用法"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"縦向きにするには画面を回転します"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"横向きにするには画面を回転します"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"分割画面を使用するにはもう 1 つのアプリをドラッグします"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"位置を変更するにはダブルタップします"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 b7ab28c44fd2..d6399e537894 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無題の番組)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP を閉じる"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全画面表示"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP を移動"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index ef9a84f4ce2f..4155236bdbc5 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"დახურვა"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"გაშლა"</string>
<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_notification_message" msgid="8854051911700302620">"თუ არ გსურთ, რომ <xliff:g id="NAME">%s</xliff:g> ამ ფუნქციას იყენებდეს, აქ შეხებით შეგიძლიათ გახსნათ პარამეტრები და გამორთოთ ის."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"აპმა შეიძლება არ იმუშაოს მეორეულ ეკრანზე."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"გასაგებია"</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="3924745395335329810">"მაქსიმალურად გამოიყენეთ <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"პორტრეტის რეჟიმზე გადასასვლელად შეატრიალეთ თქვენი ეკრანი"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"ლანდშაფტის რეჟიმზე გადასასვლელად შეატრიალეთ თქვენი ეკრანი"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"ეკრანის გასაყოფად ჩავლებით გადაიტანეთ სხვა აპში"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"პოზიციის შესაცვლელად ორჯერ შეეხეთ"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"გასაგებია"</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 1bf4b8ebdcda..8d7bee8f1398 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(პროგრამის სათაურის გარეშე)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP-ის დახურვა"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"სრულ ეკრანზე"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP გადატანა"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 13f3a4eed8c5..f93dab5b216f 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Жабу"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Жаю"</string>
<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_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>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Қолданба қосымша дисплейде жұмыс істемеуі мүмкін."</string>
@@ -68,7 +71,15 @@
<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="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string>
- <string name="got_it" msgid="4428750913636945527">"Түсінікті"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> мүмкіндіктерін барынша пайдаланыңыз"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Көлденеңінен ашу үшін экранды бұрыңыз."</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Тігінен ашу үшін экранды бұрыңыз."</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Экранды бөлу үшін басқа қолданбаға өтіңіз."</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Қалпын өзгерту үшін екі рет түртіңіз."</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Түсінікті"</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 8f1e725e79e2..05bdcc71f293 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Атаусыз бағдарлама)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP жабу"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Толық экран"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP клипін жылжыту"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 134d3c2334f0..3fedb241cbad 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"បិទ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ពង្រីក"</string>
<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_notification_message" msgid="8854051911700302620">"ប្រសិនបើ​អ្នក​មិន​ចង់​ឲ្យ <xliff:g id="NAME">%s</xliff:g> ប្រើ​មុខងារ​នេះ​ សូមចុច​​បើក​ការកំណត់ រួច​បិទ​វា។"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"កម្មវិធីនេះ​ប្រហែល​ជាមិនដំណើរការ​នៅលើ​អេក្រង់បន្ទាប់បន្សំទេ។"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"យល់ហើយ"</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="3924745395335329810">"ទទួលបានអត្ថប្រយោជន៍ច្រើនបំផុតពី <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"បង្វិលអេក្រង់របស់អ្នកទៅទិសដៅបញ្ឈរ"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"បង្វិលអេក្រង់របស់អ្នកទៅទិសដៅផ្ដេក"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"អូសនៅក្នុងកម្មវិធីផ្សេងទៀត ដើម្បីប្រើមុខងារ​បំបែកអេក្រង់"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"ចុចពីរដង ដើម្បីប្ដូរទីតាំង"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"យល់ហើយ"</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 b55997056e66..e8315163ad46 100644
--- a/libs/WindowManager/Shell/res/values-km/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(កម្មវិធី​គ្មានចំណងជើង)"</string>
<string name="pip_close" msgid="9135220303720555525">"បិទ PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ពេញអេក្រង់"</string>
+ <string name="pip_move" msgid="1544227837964635439">"ផ្លាស់ទី PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index c8b3389a3a60..49203e5dc10f 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ಮುಚ್ಚಿ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ವಿಸ್ತೃತಗೊಳಿಸು"</string>
<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_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> ಈ ವೈಶಿಷ್ಟ್ಯ ಬಳಸುವುದನ್ನು ನೀವು ಬಯಸದಿದ್ದರೆ, ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಲು ಮತ್ತು ಅದನ್ನು ಆಫ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್‌ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"ಅರ್ಥವಾಯಿತು"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> ನಿಂದ ಹೆಚ್ಚಿನ ಪ್ರಯೋಜನವನ್ನು ಪಡೆದುಕೊಳ್ಳಿ"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಪೋರ್ಟ್ರೇಟ್‌ಗೆ ತಿರುಗಿಸಿ"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್‌ಗೆ ತಿರುಗಿಸಿ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಬಳಸಲು ಬೇರೊಂದು ಆ್ಯಪ್ ಅನ್ನು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"ಸರಿಹೊಂದಿಸಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ಸರಿ"</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 9d3942fa4dd3..305ef668cb58 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP ಮುಚ್ಚಿ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ಪೂರ್ಣ ಪರದೆ"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP ಅನ್ನು ಸರಿಸಿ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index b29612e337f2..6164ed90c7b8 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"닫기"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"펼치기"</string>
<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>에서 PIP 사용 중"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g>에서 이 기능이 사용되는 것을 원하지 않는 경우 탭하여 설정을 열고 기능을 사용 중지하세요."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"앱이 보조 디스플레이에서 작동하지 않을 수도 있습니다."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"확인"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> 최대한 활용하기"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"세로 모드로 화면을 회전합니다."</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"가로 모드로 화면을 회전합니다."</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"화면 분할을 사용하려면 다른 앱에 드래그합니다."</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"두 번 탭하여 위치를 조정합니다."</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"확인"</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 46d6ad4e0b0f..76b0adfb3d88 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(제목 없는 프로그램)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP 닫기"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"전체화면"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP 이동"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 0c64c7639327..29091fbf641e 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Жабуу"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Жайып көрсөтүү"</string>
<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_notification_message" msgid="8854051911700302620">"Эгер <xliff:g id="NAME">%s</xliff:g> колдонмосу бул функцияны пайдаланбасын десеңиз, жөндөөлөрдү ачып туруп, аны өчүрүп коюңуз."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Колдонмо кошумча экранда иштебей коюшу мүмкүн."</string>
@@ -44,7 +47,7 @@
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Үстүнкү экранды 30%"</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>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Чыгуу үчүн экранды ылдый жагынан өйдө сүрүңүз же колдонмонун өйдө жагын басыңыз"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Бир кол режимин баштоо"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Бир кол режиминен чыгуу"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> калкып чыкма билдирмелер жөндөөлөрү"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Түшүндүм"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> колдонмосунун бардык мүмкүнчүлүктөрүн колдонуңуз"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Экранды тигинен буруңуз"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Экранды туурасынан буруңуз"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Экранды бөлүү үчүн башка колдонмого сүйрөңүз"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Ракурсту өзгөртүү үчүн эки жолу басыңыз"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Түшүндүм"</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 d5d1d7ef914e..57b955a7c5d4 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Аталышы жок программа)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP\'ти жабуу"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Толук экран"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP\'ти жылдыруу"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 5ccf164b3d67..20b15ee55897 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ປິດ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ຂະຫຍາຍ"</string>
<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_notification_message" msgid="8854051911700302620">"ຫາກທ່ານບໍ່ຕ້ອງການ <xliff:g id="NAME">%s</xliff:g> ໃຫ້ໃຊ້ຄຸນສົມບັດນີ້, ໃຫ້ແຕະເພື່ອເປີດການຕັ້ງຄ່າ ແລ້ວປິດມັນໄວ້."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"ແອັບອາດບໍ່ສາມາດໃຊ້ໄດ້ໃນໜ້າຈໍທີສອງ."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"ເຂົ້າໃຈແລ້ວ"</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="3924745395335329810">"ໃຊ້ປະໂຫຍດສູງສຸດຈາກ <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"ໝຸນໜ້າຈໍຂອງທ່ານເປັນລວງຕັ້ງ"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"ໝຸນໜ້າຈໍຂອງທ່ານເປັນລວງນອນ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"ລາກແອັບອື່ນເຂົ້າມາເພື່ອໃຊ້ການແບ່ງໜ້າຈໍ"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"ແຕະສອງເທື່ອເພື່ອປ່ຽນຕຳແໜ່ງ"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ເຂົ້າໃຈແລ້ວ"</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 f6362c120b9f..cbea84ea7ea2 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ໂປຣແກຣມບໍ່ມີຊື່)"</string>
<string name="pip_close" msgid="9135220303720555525">"ປິດ PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ເຕັມໜ້າຈໍ"</string>
+ <string name="pip_move" msgid="1544227837964635439">"ຍ້າຍ PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 1433312ded60..80f281573f2b 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Uždaryti"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Išskleisti"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Praleisti ir eiti į kitą"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Praleisti ir eiti į ankstesnį"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Pakeisti dydį"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Paslėpti"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Programa gali neveikti antriniame ekrane."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Supratau"</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="3924745395335329810">"Išnaudokite visas „<xliff:g id="APP_NAME">%s</xliff:g>“ galimybes"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Pasukite ekraną stačiai"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Pasukite ekraną gulsčiai"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Vilkite kitoje programoje, kad galėtumėte naudoti išskaidyto ekrano režimą"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Keiskite vietą dukart paliesdami"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Supratau"</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 e4695a05f038..81716a609fc5 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Visas ekranas"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Perkelti PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index fb297b8f2990..a66b29e484fe 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Aizvērt"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Izvērst"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pāriet uz nākamo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pāriet uz iepriekšējo"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Mainīt lielumu"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Paslēpt"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Labi"</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="3924745395335329810">"Izmantojiet visas lietotnes <xliff:g id="APP_NAME">%s</xliff:g> sniegtās iespējas"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Pagrieziet ekrānu, lai iestatītu portreta režīmu"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Pagrieziet ekrānu, lai iestatītu ainavas režīmu"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Velciet uz citu lietotni, lai izmantotu dalītu ekrānu"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Veiciet dubultskārienu, lai mainītu pozīciju"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Labi"</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 f2b037fbeeee..5295cd230591 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Pilnekrāna režīms"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Pārvietot attēlu attēlā"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 80b33293af34..9502a898c3d2 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Затвори"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Проширете"</string>
<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_notification_message" msgid="8854051911700302620">"Ако не сакате <xliff:g id="NAME">%s</xliff:g> да ја користи функцијава, допрете за да ги отворите поставките и да ја исклучите."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликацијата може да не функционира на друг екран."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Сфатив"</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="3924745395335329810">"Извлечете го максимумот од <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Ротирајте го екранот во портрет"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Ротирајте го екранот во пејзаж"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Повлечете друга апликација за да користите поделен екран"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Допрете двапати за да преместите"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Сфатив"</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 25dc764f4d5e..fa48a6cc1846 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без наслов)"</string>
<string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Цел екран"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Премести PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 5d47911137ec..7f2c977ebcbb 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"അവസാനിപ്പിക്കുക"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"വികസിപ്പിക്കുക"</string>
<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_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> ഈ ഫീച്ചർ ഉപയോഗിക്കേണ്ടെങ്കിൽ, ടാപ്പ് ചെയ്‌ത് ക്രമീകരണം തുറന്ന് അത് ഓഫാക്കുക."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"രണ്ടാം ഡിസ്‌പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
@@ -66,9 +69,17 @@
<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="notification_bubble_title" msgid="6082910224488253378">"ബബിൾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
- <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബ്ൾ ഡിസ്മിസ് ചെയ്തു."</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
<string name="restart_button_description" msgid="5887656107651190519">"ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്‌ത് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ടാപ്പ് ചെയ്യുക."</string>
- <string name="got_it" msgid="4428750913636945527">"മനസ്സിലായി"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> പരമാവധി പ്രയോജനപ്പെടുത്തുക"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"പോർട്രെയ്‌റ്റിന് നിങ്ങളുടെ സ്ക്രീൻ റൊട്ടേറ്റ് ചെയ്യുക"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"ലാൻഡ്‌സ്‌കേപ്പിന് നിങ്ങളുടെ സ്ക്രീൻ റൊട്ടേറ്റ് ചെയ്യുക"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"സ്‌ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കാൻ മറ്റൊരു ആപ്പിൽ വലിച്ചിടുക"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"സ്ഥാനം മാറ്റാൻ ഡബിൾ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"മനസ്സിലായി"</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 c74e0bbfaa5b..533375751378 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(പേരില്ലാത്ത പ്രോഗ്രാം)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP അടയ്ക്കുക"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"പൂര്‍ണ്ണ സ്ക്രീന്‍"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP നീക്കുക"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index a5e7f957b829..ce4f4e171200 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Хаах"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Дэлгэх"</string>
<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_notification_message" msgid="8854051911700302620">"Та <xliff:g id="NAME">%s</xliff:g>-д энэ онцлогийг ашиглуулахыг хүсэхгүй байвал тохиргоог нээгээд, үүнийг унтраана уу."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Апп хоёрдогч дэлгэцэд ажиллахгүй."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Ойлголоо"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g>-г бүрэн ашиглаарай"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Дэлгэцээ босоо байрлал руу эргүүлнэ үү"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Дэлгэцээ хөндлөн байрлал руу эргүүлнэ үү"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Дэлгэц хуваахыг ашиглахын тулд өөр аппыг чирнэ үү"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Дахин байрлуулахын тулд хоёр товшино уу"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ойлголоо"</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 55519d462b69..ca1d27f29cdf 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Гарчиггүй хөтөлбөр)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP-г хаах"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Бүтэн дэлгэц"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP-г зөөх"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 0450189c515d..2a7ecbe4e8d2 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"बंद करा"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"विस्तृत करा"</string>
<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_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g>ने हे वैशिष्ट्य वापरू नये असे तुम्हाला वाटत असल्यास, सेटिंग्ज उघडण्यासाठी टॅप करा आणि ते बंद करा."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"दुसऱ्या डिस्प्लेवर अ‍ॅप कदाचित चालणार नाही."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"समजले"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> चा पुरेपूर वापर करा"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"तुमची स्क्रीन पोर्ट्रेटवर फिरवा"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"तुमची स्क्रीन लॅंडस्केपवर फिरवा"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"स्प्लिट स्क्रीन वापरण्यासाठी दुसऱ्या ॲपमध्ये ड्रॅग करा"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"जागा बदलण्यासाठी दोनदा टॅप करा"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"समजले"</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 ad2cfc6035c2..212bd21db344 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षक नसलेला कार्यक्रम)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP बंद करा"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रीन"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP हलवा"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index c0c1cbd6ed3c..c1ec74f9fd39 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Tutup"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Kembangkan"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Langkau ke seterusnya"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Langkau ke sebelumnya"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah saiz"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Sembunyikan"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Manfaatkan <xliff:g id="APP_NAME">%s</xliff:g> sepenuhnya"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Putar skrin anda kepada potret"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Putar skrin anda kepada landskap"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Seret ke dalam apl lain untuk menggunakan skrin pisah"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Ketik dua kali untuk menempatkan semula"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 b2d7214381ef..ce2912650da7 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tiada tajuk)"</string>
<string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Skrin penuh"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Alihkan PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 0d78f89d9e54..fc1868fe99c5 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -20,14 +20,17 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ပိတ်ရန်"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ချဲ့ရန်"</string>
<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_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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"ဤအက်ပ်အနေဖြင့် ဒုတိယဖန်သားပြင်ပေါ်တွင် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"ရပြီ"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> ကို အကောင်းဆုံးအသုံးချခြင်း"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"သင့်စခရင်ကို ဒေါင်လိုက်လှည့်နိုင်သည်"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"သင့်စခရင်ကို အလျားလိုက်လှည့်နိုင်သည်"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးရန် အက်ပ်နောက်တစ်ခုကို ဖိဆွဲနိုင်သည်"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"နေရာပြန်ချရန် နှစ်ချက်တို့နိုင်သည်"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ရပြီ"</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 9569dc4cbeea..4847742130ba 100644
--- a/libs/WindowManager/Shell/res/values-my/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
@@ -17,8 +17,9 @@
<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_fullscreen" msgid="7278047353591302554">"မျက်နှာပြင် အပြည့်"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP ရွှေ့ရန်"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index fab0c0cacef4..3eda48a6f066 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Lukk"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Vis"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Hopp til neste"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Hopp til forrige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Endre størrelse"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Oppbevar"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Greit"</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="3924745395335329810">"Få mest mulig ut av <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Roter skjermen til stående format"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Roter skjermen til liggende format"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Dra inn en annen app for å bruke delt skjerm"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Dobbelttrykk for å flytte"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Greit"</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 8a7f315606ad..7cef11c424ce 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uten tittel)"</string>
<string name="pip_close" msgid="9135220303720555525">"Lukk PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Fullskjerm"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Flytt BIB"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index dfa364a763a8..25db78eec69d 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"बन्द गर्नुहोस्"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"विस्तृत गर्नुहोस्"</string>
<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> Picture-in-picture मा छ"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"तपाईं <xliff:g id="NAME">%s</xliff:g> ले सुविधा प्रयोग नगरोस् भन्ने चाहनुहुन्छ भने ट्याप गरेर सेटिङहरू खोल्नुहोस् र यसलाई निष्क्रिय पार्नुहोस्।"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"यो एपले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"बुझेँ"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> बाट बढीभन्दा बढी फाइदा लिनुहोस्"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"आफ्नो स्क्रिन पोर्ट्रेट मोडमा रोटेट गर्नुहोस्"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"आफ्नो स्क्रिन ल्यान्डस्केप मोडमा रोटेट गर्नुहोस्"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"तपाईं स्प्लिट स्क्रिन मोड प्रयोग गर्न चाहनुहुन्छ भने अर्को एप ड्रयाग एन्ड ड्रप गर्नुहोस्"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"एप यताउता सार्न डबल ट्याप गर्नुहोस्"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"बुझेँ"</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 87fa3279f05e..684d11490be3 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षकविहीन कार्यक्रम)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP लाई बन्द गर्नुहोस्"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रिन"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP सार्नुहोस्"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 0601f153b330..8f74258ebfeb 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Sluiten"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Uitvouwen"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Doorgaan naar volgende"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Teruggaan naar vorige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Formaat aanpassen"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Verbergen"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"App werkt mogelijk niet op een secundair scherm."</string>
@@ -43,10 +46,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bovenste scherm 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Bovenste scherm 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Onderste scherm op volledig scherm"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met één hand gebruiken"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met 1 hand gebruiken"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Als je wilt afsluiten, swipe je omhoog vanaf de onderkant van het scherm of tik je ergens boven de app"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met één hand starten"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bediening met één hand afsluiten"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met 1 hand starten"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bediening met 1 hand afsluiten"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Instellingen voor <xliff:g id="APP_NAME">%1$s</xliff:g>-bubbels"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overloop"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Weer toevoegen aan stack"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Haal het maximale uit <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Draai je scherm naar de staande stand"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Draai je scherm naar de liggende stand"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Sleep een andere app hier naartoe om het scherm te splitsen"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Dubbeltik om te herpositioneren"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 df3809e5d6c6..8562517bd58b 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Naamloos programma)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP sluiten"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Volledig scherm"</string>
+ <string name="pip_move" msgid="1544227837964635439">"SIS verplaatsen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 50d20076a26f..a7092971e9bb 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ବଢ଼ାନ୍ତୁ"</string>
<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_notification_message" msgid="8854051911700302620">"ଏହି ବୈଶିଷ୍ଟ୍ୟ <xliff:g id="NAME">%s</xliff:g> ବ୍ୟବହାର ନକରିବାକୁ ଯଦି ଆପଣ ଚାହାଁନ୍ତି, ସେଟିଙ୍ଗ ଖୋଲିବାକୁ ଟାପ୍‍ କରନ୍ତୁ ଏବଂ ଏହା ଅଫ୍‍ କରିଦିଅନ୍ତୁ।"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ କାମ ନକରିପାରେ।"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"ବୁଝିଗଲି"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g>ରୁ ସବୁଠାରୁ ଅଧିକ ସୁବିଧା ପାଆନ୍ତୁ"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"ପୋଟ୍ରେଟ ସ୍ଥିତି ପାଇଁ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ରୋଟେଟ କରନ୍ତୁ"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"ଲ୍ୟାଣ୍ଡସ୍କେପ ସ୍ଥିତି ପାଇଁ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ରୋଟେଟ କରନ୍ତୁ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ପାଇଁ ଅନ୍ୟ ଏକ ଆପକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"ରିପୋଜିସନ ପାଇଁ ଦୁଇଥର ଟାପ କରନ୍ତୁ"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ବୁଝିଗଲି"</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 295a5c4ee1ce..f8bc01642d88 100644
--- a/libs/WindowManager/Shell/res/values-or/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(କୌଣସି ଟାଇଟଲ୍‍ ପ୍ରୋଗ୍ରାମ୍‍ ନାହିଁ)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIPକୁ ମୁଭ କରନ୍ତୁ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index dd3d26e56b4c..8c1567868a83 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ਬੰਦ ਕਰੋ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ਵਿਸਤਾਰ ਕਰੋ"</string>
<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_notification_message" msgid="8854051911700302620">"ਜੇਕਰ ਤੁਸੀਂ ਨਹੀਂ ਚਾਹੁੰਦੇ ਕਿ <xliff:g id="NAME">%s</xliff:g> ਐਪ ਇਸ ਵਿਸ਼ੇਸ਼ਤਾ ਦੀ ਵਰਤੋਂ ਕਰੇ, ਤਾਂ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਇਸਨੂੰ ਬੰਦ ਕਰੋ।"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇ \'ਤੇ ਕੰਮ ਨਾ ਕਰੇ।"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"ਸਮਝ ਲਿਆ"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> ਦਾ ਵੱਧ ਤੋਂ ਵੱਧ ਲਾਹਾ ਲਓ"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"ਪੋਰਟਰੇਟ ਕਰਨ ਲਈ ਆਪਣੀ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁਮਾਓ"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"ਲੈਂਡਸਕੇਪ ਕਰਨ ਲਈ ਆਪਣੀ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁਮਾਓ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਵਿੱਚ ਘਸੀਟੋ"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"ਜਗ੍ਹਾ ਬਦਲਣ ਲਈ ਡਬਲ ਟੈਪ ਕਰੋ"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ਸਮਝ ਲਿਆ"</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 e32895a9a239..1667e5fc6eac 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ਸਿਰਲੇਖ-ਰਹਿਤ ਪ੍ਰੋਗਰਾਮ)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP ਬੰਦ ਕਰੋ"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP ਨੂੰ ਲਿਜਾਓ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index ca2bdcb270da..80979952daca 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zamknij"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Rozwiń"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Dalej"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Wstecz"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmień rozmiar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Przenieś do schowka"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacja może nie działać na dodatkowym ekranie."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Wykorzystaj wszystkie możliwości aplikacji <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Obróć ekran do orientacji pionowej"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Obróć ekran do orientacji poziomej"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Przeciągnij drugą aplikację, aby podzielić ekran"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Kliknij dwukrotnie, aby zmienić położenie"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 286fd7b2ff0f..28bf66a7ee1b 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Pełny ekran"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Przenieś PIP"</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 bdd0d4b8baec..644e80e4d598 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -18,8 +18,9 @@
<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">"Fechar"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Abrir"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Ok"</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="3924745395335329810">"Aproveite o app <xliff:g id="APP_NAME">%s</xliff:g> ao máximo"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Gire a tela para o modo retrato"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Gire a tela para o modo paisagem"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Arraste outro app para usar a tela dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Toque duas vezes para reposicionar"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</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 57edcdf74cf4..27626b8ecfd6 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Mover picture-in-picture"</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 6661b0506c5e..fac38a5823dc 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Fechar"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Mudar para o seguinte"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Mudar para o anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Armazenar"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"A app pode não funcionar num ecrã secundário."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Tire o máximo partido da app <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Rode o ecrã para vertical"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Rode o ecrã para horizontal"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Arraste para utilizar o ecrã dividido"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Toque duas vezes para reposicionar"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 9372e0f637cb..a2010cee9e03 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Ecrã inteiro"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Mover Ecrã no ecrã"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index bdd0d4b8baec..644e80e4d598 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -18,8 +18,9 @@
<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">"Fechar"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Abrir"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Ok"</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="3924745395335329810">"Aproveite o app <xliff:g id="APP_NAME">%s</xliff:g> ao máximo"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Gire a tela para o modo retrato"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Gire a tela para o modo paisagem"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Arraste outro app para usar a tela dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Toque duas vezes para reposicionar"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</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 57edcdf74cf4..27626b8ecfd6 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Tela cheia"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Mover picture-in-picture"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 9112543c8f60..e7d96cb20141 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Închideți"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Extindeți"</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_menu_title" msgid="5393619322111827096">"Meniu"</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>
@@ -28,6 +29,8 @@
<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="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="forced_resizable_secondary_display" msgid="1768046938673582671">"Este posibil ca aplicația să nu funcționeze pe un ecran secundar."</string>
@@ -70,5 +73,13 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Beneficiați de toate avantajele <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Rotiți ecranul în orientarea portret"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Rotiți ecranul în orientarea peisaj"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Trageți în altă aplicație pentru a folosi ecranul împărțit"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Atingeți de două ori pentru a repoziționa"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 9438e4955b68..18e29a60191f 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Ecran complet"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Mutați fereastra PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 5120136e3c68..046497d32d3b 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Закрыть"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Развернуть"</string>
<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_notification_message" msgid="8854051911700302620">"Чтобы отключить эту функцию для приложения \"<xliff:g id="NAME">%s</xliff:g>\", перейдите в настройки."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Приложение может не работать на дополнительном экране"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"ОК"</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="3924745395335329810">"Используйте все возможности приложения \"<xliff:g id="APP_NAME">%s</xliff:g>\""</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Поверните экран в вертикальное положение."</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Поверните экран в горизонтальное положение."</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Перетащите сюда другое приложение, чтобы использовать разделение экрана."</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Нажмите дважды, чтобы переместить окно."</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ОК"</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 24785aa7e184..d119240adc4d 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Без названия)"</string>
<string name="pip_close" msgid="9135220303720555525">"\"Кадр в кадре\" – выйти"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Во весь экран"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Переместить PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index e1d9a825a004..0c604ff1140f 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"වසන්න"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"දිග හරින්න"</string>
<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_notification_message" msgid="8854051911700302620">"ඔබට <xliff:g id="NAME">%s</xliff:g> මෙම විශේෂාංගය භාවිත කිරීමට අවශ්‍ය නැති නම්, සැකසීම් විවෘත කිරීමට තට්ටු කර එය ක්‍රියාවිරහිත කරන්න."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"යෙදුම ද්විතියික සංදර්ශකයක ක්‍රියා නොකළ හැකිය."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"තේරුණා"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> වෙතින් උපරිම ප්‍රයෝජන ගන්න"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"ඔබගේ තිරය ප්‍රතිමූර්තියට කරකවන්න"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"ඔබගේ තිරය භූදර්ශනයට කරකවන්න"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"බෙදුම් තිරය භාවිත කිරීමට තවත් යෙදුමක් මෙතැනට අදින්න"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"නැවත ස්ථානගත කිරීමට දෙවරක් තට්ටු කරන්න"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"තේරුණා"</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 62ee6d4f44d2..86769b6ee849 100644
--- a/libs/WindowManager/Shell/res/values-si/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(මාතෘකාවක් නැති වැඩසටහන)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIP වසන්න"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"සම්පූර්ණ තිරය"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP ගෙන යන්න"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index c88099b35c0d..f65108093e69 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zavrieť"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Rozbaliť"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskočiť na ďalšie"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskočiť na predchádzajúce"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmeniť veľkosť"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Skryť"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikácia nemusí fungovať na sekundárnej obrazovke."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Dobre"</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="3924745395335329810">"Využívajte aplikáciu <xliff:g id="APP_NAME">%s</xliff:g> naplno"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Otočenie obrazovky na výšku"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Otočenie obrazovky na šírku"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Ak chcete použiť rozdelenú obrazovku, presuňte do inej aplikácie"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Dvojitým klepnutím premiestníte"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Dobre"</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 a7a515cdc61c..6f6ccb703cf6 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Presunúť obraz v obraze"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 42d7be7a146d..104b9a51f625 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zapri"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Razširi"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na naslednjega"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prejšnjega"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Spremeni velikost"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Zakrij"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija morda ne bo delovala na sekundarnem zaslonu."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Razumem"</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="3924745395335329810">"Izkoristite aplikacijo <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Zasuk zaslona v pokončni položaj"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Zasuk zaslona v ležeči položaj"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Za razdeljeni zaslon povlecite sem še eno aplikacijo."</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Dvakrat se dotaknite za prestavljanje."</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"V redu"</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 fe5c9ae5d2a8..837794ad4be7 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Celozaslonsko"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Premakni sliko v sliki"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 1f373b53d0a4..d0d485ac2f4c 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Mbyll"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Zgjero"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Kalo te tjetra"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Kalo tek e mëparshmja"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ndrysho përmasat"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Fshih"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacioni mund të mos funksionojë në një ekran dytësor."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"E kuptova"</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="3924745395335329810">"Përfito sa më shumë nga <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Rrotulloje ekranin vertikalisht"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Rrotulloje ekranin horizontalisht"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Zvarrite në një aplikacion tjetër për të përdorur ekranin e ndarë"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Trokit dy herë për ta risistemuar"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"E kuptova"</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 1d5583b2c826..107870d0489f 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program pa titull)"</string>
<string name="pip_close" msgid="9135220303720555525">"Mbyll PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ekrani i plotë"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Zhvendos PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 2bbbbf9e0714..3b4418153561 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Затвори"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Прошири"</string>
<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_notification_message" msgid="8854051911700302620">"Ако не желите да <xliff:g id="NAME">%s</xliff:g> користи ову функцију, додирните да бисте отворили подешавања и искључили је."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликација можда неће функционисати на секундарном екрану."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Важи"</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="3924745395335329810">"Искористите апликацију <xliff:g id="APP_NAME">%s</xliff:g> на најбољи начин"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Ротирајте екран у усправни положај"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Ротирајте екран у водоравни положај"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Превуците другу апликацију да бисте користили подељени екран"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Двапут додирните ради премештања"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Важи"</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 62ad1e8f6e69..ee5690ba4a9a 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програм без наслова)"</string>
<string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Цео екран"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Премести слику у слици"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 692b5ed8576f..b066c9fd96c8 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Stäng"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Utöka"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Hoppa till nästa"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Hoppa till föregående"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ändra storlek"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Utför stash"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen kanske inte fungerar på en sekundär skärm."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Få ut mesta möjliga av <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Rotera skärmen till stående läge"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Rotera skärmen till liggande läge"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Dra en annan app hit för att dela upp skärmen"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Tryck snabbt två gånger för att flytta"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 74fb590c3e4d..7355adf51e97 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Helskärm"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Flytta BIB"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 61c95ee183a0..ea9d8859e22e 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Funga"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Panua"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ruka ufikie inayofuata"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ruka ufikie iliyotangulia"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Badilisha ukubwa"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ficha"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Huenda programu isifanye kazi kwenye dirisha lingine."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Nimeelewa"</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="3924745395335329810">"Nufaika zaidi na <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Zungusha skrini iwe wima"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Zungusha skrini yako iwe mlalo"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Buruta ndani programu nyingine ili utumie skrini iliyogawanyika"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Gusa mara mbili ili uweke kwenye nafasi yake"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Nimeelewa"</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 cf0d8a9b3910..0ee28416137a 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Skrini nzima"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Kuhamisha PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 32a925a7994e..30b9813883fb 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"மூடு"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"விரி"</string>
<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_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> இந்த அம்சத்தைப் பயன்படுத்த வேண்டாம் என நினைத்தால் இங்கு தட்டி அமைப்புகளைத் திறந்து இதை முடக்கவும்."</string>
@@ -28,6 +29,8 @@
<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_stash" msgid="4060775037619702641">"Stash"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"இரண்டாம்நிலைத் திரையில் ஆப்ஸ் வேலை செய்யாமல் போகக்கூடும்."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"சரி"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> ஆப்ஸில் அதிகப் பலன்களைப் பெறுங்கள்"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"திரையைப் போர்ட்ரெய்ட் பயன்முறைக்குச் சுழற்றலாம்"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"திரையை லேண்ட்ஸ்கேப் பயன்முறைக்குச் சுழற்றலாம்"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"திரைப் பிரிப்பைப் பயன்படுத்த மற்றொரு ஆப்ஸை இழுக்கலாம்"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"இடம் மாற்ற இருமுறை தட்டலாம்"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"சரி"</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 8bca46314e30..8bcc43bea59a 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(தலைப்பு இல்லை)"</string>
<string name="pip_close" msgid="9135220303720555525">"PIPஐ மூடு"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"முழுத்திரை"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIPபை நகர்த்து"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 3db12e7d5661..fc4ab39bfab9 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"మూసివేయి"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"విస్తరింపజేయి"</string>
<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_notification_message" msgid="8854051911700302620">"<xliff:g id="NAME">%s</xliff:g> ఈ లక్షణాన్ని ఉపయోగించకూడదు అని మీరు అనుకుంటే, సెట్టింగ్‌లను తెరవడానికి ట్యాప్ చేసి, దీన్ని ఆఫ్ చేయండి."</string>
@@ -28,21 +29,23 @@
<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_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_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"యాప్‌లో స్క్రీన్ విభజనకు మద్దతు లేదు."</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="accessibility_action_divider_left_full" msgid="1792313656305328536">"ఎడమవైపు పూర్తి స్క్రీన్"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ఎడమవైపు ఫుల్-స్క్రీన్‌"</string>
<string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"ఎడమవైపు 70%"</string>
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ఎడమవైపు 50%"</string>
<string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ఎడమవైపు 30%"</string>
- <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"కుడివైపు పూర్తి స్క్రీన్"</string>
- <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ఎగువ పూర్తి స్క్రీన్"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"కుడివైపు ఫుల్-స్క్రీన్‌"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ఎగువ ఫుల్-స్క్రీన్‌"</string>
<string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ఎగువ 70%"</string>
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ఎగువ 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ఎగువ 30%"</string>
- <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"దిగువ పూర్తి స్క్రీన్"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"దిగువ ఫుల్-స్క్రీన్‌"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"వన్-హ్యాండెడ్ మోడ్‌ను ఉపయోగించడం"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"నిష్క్రమించడానికి, స్క్రీన్ కింది భాగం నుండి పైకి స్వైప్ చేయండి లేదా యాప్ పైన ఎక్కడైనా ట్యాప్ చేయండి"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"వన్-హ్యాండెడ్ మోడ్‌ను ప్రారంభిస్తుంది"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"అర్థమైంది"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> నుండి మరిన్ని పొందండి"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"మీ స్క్రీన్‌ను పోర్ట్రెయిట్‌కు తిప్పండి"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"మీ స్క్రీన్‌ను ల్యాండ్‌స్కేప్‌కు తిప్పండి"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"స్ప్లిట్ స్క్రీన్‌ను ఉపయోగించడానికి మరొక యాప్‌లోకి లాగండి"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"స్థానాన్ని మార్చడానికి రెండుసార్లు నొక్కండి"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"అర్థమైంది"</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 47489efbc4c2..6e80bd7b3a0c 100644
--- a/libs/WindowManager/Shell/res/values-te/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
@@ -20,5 +20,6 @@
<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_fullscreen" msgid="7278047353591302554">"పూర్తి స్క్రీన్"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ఫుల్-స్క్రీన్‌"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIPను తరలించండి"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 7df76e84cbb7..be0d61bbd6ad 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ปิด"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ขยาย"</string>
<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_notification_message" msgid="8854051911700302620">"หากคุณไม่ต้องการให้ <xliff:g id="NAME">%s</xliff:g> ใช้ฟีเจอร์นี้ ให้แตะเพื่อเปิดการตั้งค่าแล้วปิดฟีเจอร์"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"แอปอาจไม่ทำงานในจอแสดงผลรอง"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"รับทราบ"</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="3924745395335329810">"ใช้ประโยชน์สูงสุดจาก <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"หมุนหน้าจอเป็นแนวตั้ง"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"หมุนหน้าจอเป็นแนวนอน"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"ลากไปไว้ในแอปอื่นเพื่อใช้การแบ่งหน้าจอ"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"แตะสองครั้งเพื่อเปลี่ยนตำแหน่ง"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"รับทราบ"</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 d3797e7c3cde..b6f63699cc00 100644
--- a/libs/WindowManager/Shell/res/values-th/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ไม่มีชื่อรายการ)"</string>
<string name="pip_close" msgid="9135220303720555525">"ปิด PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"เต็มหน้าจอ"</string>
+ <string name="pip_move" msgid="1544227837964635439">"ย้าย PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index d6c2784f764f..cb8bb1d00407 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Isara"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Palawakin"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Lumaktaw sa susunod"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Lumaktaw sa nakaraan"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"I-resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"I-stash"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Maaaring hindi gumana ang app sa pangalawang display."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"Sulitin ang <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"I-rotate sa portrait ang iyong screen"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"I-rotate sa landscape ang iyong screen"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Mag-drag ng ibang app para gamitin ang split screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"I-double tap para baguhin ang puwesto"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 b01c1115cd34..71ca2306ea03 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Full screen"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Ilipat ang PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 47d5966549ef..7f78fe91d80c 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Kapat"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Genişlet"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Sonrakine atla"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Öncekine atla"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Yeniden boyutlandır"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Depola"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Uygulama ikincil ekranda çalışmayabilir."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Anladım"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> uygulamasından en iyi şekilde yararlanma"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Dikey görüntüleme için ekranınızı döndürün"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Yatay görüntüleme için ekranınızı döndürün"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Bölünmüş ekran kullanmak için başka bir uygulamayı sürükleyin"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Yeniden konumlandırmak için iki kez dokunun"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</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 c92c4d02f465..e6ae7f167758 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIP\'yi taşı"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
index 7920fd237a08..558ec51752b9 100644
--- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
@@ -16,7 +16,19 @@
-->
<resources>
<!-- The dimensions to user for picture-in-picture action buttons. -->
- <dimen name="picture_in_picture_button_width">100dp</dimen>
- <dimen name="picture_in_picture_button_start_margin">-50dp</dimen>
+ <dimen name="pip_menu_button_size">40dp</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>
+ <dimen name="pip_menu_border_radius">4dp</dimen>
+ <dimen name="pip_menu_outer_space">24dp</dimen>
+
+ <!-- outer space minus border width -->
+ <dimen name="pip_menu_outer_space_frame">20dp</dimen>
+
+ <dimen name="pip_menu_arrow_size">24dp</dimen>
+ <dimen name="pip_menu_arrow_elevation">5dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index c57f16f059df..08fc0234249e 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Закрити"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Розгорнути"</string>
<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_notification_message" msgid="8854051911700302620">"Щоб додаток <xliff:g id="NAME">%s</xliff:g> не використовував цю функцію, вимкніть її в налаштуваннях."</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"Додаток може не працювати на додатковому екрані."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Зрозуміло"</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="3924745395335329810">"Використовуйте всі можливості додатка <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Змініть орієнтацію екрана на портретну"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Змініть орієнтацію екрана на альбомну"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Щоб перейти в режим розділення екрана, перетягніть інший додаток"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Двічі торкніться, щоб перемістити"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"ОK"</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 74d4723d7850..97e1f09844fa 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без назви)"</string>
<string name="pip_close" msgid="9135220303720555525">"Закрити PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"На весь екран"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Перемістити картинку в картинці"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 97a22e77c069..cea12e52f5a1 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"بند کریں"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"پھیلائیں"</string>
<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_notification_message" msgid="8854051911700302620">"اگر آپ نہیں چاہتے ہیں کہ <xliff:g id="NAME">%s</xliff:g> اس خصوصیت کا استعمال کرے تو ترتیبات کھولنے کے لیے تھپتھپا کر اسے آف کرے۔"</string>
@@ -28,6 +29,8 @@
<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_stash" msgid="4060775037619702641">"Stash"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"سمجھ آ گئی"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> سے بھر پور فائدہ حاصل کریں"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"اپنی سکرین کو پورٹریٹ میں گھمائيں"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"اپنی اسکرین کو لینڈ اسکیپ میں گھمائیں"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"اسپلٹ اسکرین استعمال کرنے کے ليے دوسری ایپ میں گھسیٹیں"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"پوزیشن تبدیل کرنے کے ليے دو بار تھپتھپائیں"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"سمجھ آ گئی"</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 317953309947..1418570f2538 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(بلا عنوان پروگرام)"</string>
<string name="pip_close" msgid="9135220303720555525">"‏PIP بند کریں"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"فُل اسکرین"</string>
+ <string name="pip_move" msgid="1544227837964635439">"‏PIP کو منتقل کریں"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 4e91e7624434..980e46ec2b63 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Yopish"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Yoyish"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Keyingisiga o‘tish"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Avvalgisiga qaytish"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Oʻlchamini oʻzgartirish"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Berkitish"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Bu ilova qo‘shimcha ekranda ishlamasligi mumkin."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"OK"</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="3924745395335329810">"<xliff:g id="APP_NAME">%s</xliff:g> ilovasidan unumli foydalaning"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Boʻyiga koʻrishuchun ekranni buring"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Eniga koʻrish uchun ekranni buring"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Ekranni boʻlish xususiyatidan foydalanish uchun boshqa ilovani bu yerga torting"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Joyini oʻzgartirish uchun ikki marta bosing"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 ae5a647301c8..31c762ef5f64 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nomsiz)"</string>
<string name="pip_close" msgid="9135220303720555525">"Kadr ichida kadr – chiqish"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Butun ekran"</string>
+ <string name="pip_move" msgid="1544227837964635439">"PIPni siljitish"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 169e986f7721..2f3379907222 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Đóng"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Mở rộng"</string>
<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_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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Chuyển tới mục tiếp theo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Chuyển về mục trước"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Đổi kích thước"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ẩn"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Ứng dụng có thể không hoạt động trên màn hình phụ."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Tôi hiểu"</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="3924745395335329810">"Khai thác tối đa <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Xoay màn hình sang chế độ dọc"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Xoay màn hình sang chế độ ngang"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Kéo vào một ứng dụng khác để dùng chế độ chia đôi màn hình"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Nhấn đúp để thay đổi vị trí"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</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 082d12596076..b46cd49c1901 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Toàn màn hình"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Di chuyển PIP (Ảnh trong ảnh)"</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 1999703ddd2a..48465c3e9ff2 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"关闭"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"展开"</string>
<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_notification_message" msgid="8854051911700302620">"如果您不想让“<xliff:g id="NAME">%s</xliff:g>”使用此功能,请点按以打开设置,然后关闭此功能。"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"应用可能无法在辅显示屏上正常运行。"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"知道了"</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="3924745395335329810">"充分利用 <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"将屏幕旋转成纵向模式"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"将屏幕旋转成横向模式"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"拖入另一个应用,即可使用分屏模式"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"点按两次即可调整位置"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</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 cb3fcf7c4c16..b6fec635a470 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(节目没有标题)"</string>
<string name="pip_close" msgid="9135220303720555525">"关闭画中画"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全屏"</string>
+ <string name="pip_move" msgid="1544227837964635439">"移动画中画窗口"</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 f82d6d5e771b..18f7fa2f61a3 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"關閉"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
<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_notification_message" msgid="8854051911700302620">"如果您不想「<xliff:g id="NAME">%s</xliff:g>」使用此功能,請輕按以開啟設定,然後停用此功能。"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示屏上運作。"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"知道了"</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="3924745395335329810">"充分善用「<xliff:g id="APP_NAME">%s</xliff:g>」"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"將螢幕旋轉為直向"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"將螢幕旋轉為橫向"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"拖入另一個應用程式即可使用分割螢幕"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"輕按兩下即可調整位置"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</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 956243ed6e6d..b5d54cb04354 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(沒有標題的節目)"</string>
<string name="pip_close" msgid="9135220303720555525">"關閉 PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
+ <string name="pip_move" msgid="1544227837964635439">"移動畫中畫"</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 596e7c717aa2..7bc48aeb9c07 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -20,6 +20,7 @@
<string name="pip_phone_close" msgid="5783752637260411309">"關閉"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
<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_notification_message" msgid="8854051911700302620">"如果你不想讓「<xliff:g id="NAME">%s</xliff:g>」使用這項功能,請輕觸開啟設定頁面,然後停用此功能。"</string>
@@ -28,6 +29,8 @@
<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_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="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示器上運作。"</string>
@@ -44,7 +47,7 @@
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"以 30% 的螢幕空間顯示頂端畫面"</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>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"如要退出,請從螢幕底部向上滑動,或輕觸應用程式上方的任何位置"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"啟動單手模式"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"結束單手模式"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」對話框的設定"</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"我知道了"</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="3924745395335329810">"充分發揮「<xliff:g id="APP_NAME">%s</xliff:g>」的各項功能"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"將螢幕轉成直向"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"將螢幕轉成橫向"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"拖進另一個應用程式即可使用分割畫面"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"輕觸兩下即可調整位置"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"我知道了"</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 08b2f4bbca89..57db7a839ea2 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
@@ -21,4 +21,5 @@
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(無標題的節目)"</string>
<string name="pip_close" msgid="9135220303720555525">"關閉子母畫面"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
+ <string name="pip_move" msgid="1544227837964635439">"移動子母畫面"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 3fed41f27c0a..5da9fa1f7ae5 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -20,6 +20,7 @@
<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_enter_split" msgid="7042877263880641911">"Faka ukuhlukanisa isikrini"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Imenyu"</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>
@@ -28,6 +29,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Yeqela kokulandelayo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Yeqela kokwangaphambilini"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Shintsha usayizi"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Yenza isiteshi"</string>
+ <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="forced_resizable_secondary_display" msgid="1768046938673582671">"Uhlelo lokusebenza kungenzeka lungasebenzi kusibonisi sesibili."</string>
@@ -70,5 +73,13 @@
<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="got_it" msgid="4428750913636945527">"Ngiyezwa"</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="3924745395335329810">"Thola okuningi ku-<xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="letterbox_education_screen_rotation_portrait_text" msgid="9005616635100891260">"Zungezisa isikrini sakho sime ngobude"</string>
+ <string name="letterbox_education_screen_rotation_landscape_text" msgid="7619609293250631225">"Zungezisa isikrini sakho sibe sime ngokuvundla"</string>
+ <string name="letterbox_education_split_screen_text" msgid="3906978051324735033">"Hudula kwenye i-app ukuze usebenzise isikrini esihlukanisiwe"</string>
+ <string name="letterbox_education_reposition_text" msgid="8597800472592539168">"Thepha kabili ukuze ubeke kabusha"</string>
+ <string name="letterbox_education_got_it" msgid="4057634570866051177">"Ngiyezwa"</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 89c7f498652d..646a488e4c35 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
@@ -21,4 +21,5 @@
<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_fullscreen" msgid="7278047353591302554">"Iskrini esigcwele"</string>
+ <string name="pip_move" msgid="1544227837964635439">"Hambisa i-PIP"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/attrs.xml b/libs/WindowManager/Shell/res/values/attrs.xml
new file mode 100644
index 000000000000..4aaeef8afcb0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ 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>
+ <declare-styleable name="LetterboxEduDialogActionLayout">
+ <attr name="icon" format="reference" />
+ <attr name="text" format="string" />
+ </declare-styleable>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index cf596f7d15dc..4606d24d1716 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -34,6 +34,10 @@
<color name="compat_controls_background">@android:color/system_neutral1_800</color>
<color name="compat_controls_text">@android:color/system_neutral1_50</color>
+ <!-- Letterbox Education -->
+ <color name="letterbox_education_accent_primary">@android:color/system_accent1_100</color>
+ <color name="letterbox_education_text_secondary">@android:color/system_neutral2_200</color>
+
<!-- GM2 colors -->
<color name="GM2_grey_200">#E8EAED</color>
<color name="GM2_grey_700">#5F6368</color>
diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml
new file mode 100644
index 000000000000..08d3cef10428
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values/colors_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <color name="tv_pip_menu_icon_focused">#0E0E0F</color>
+ <color name="tv_pip_menu_icon_unfocused">#E8EAED</color>
+ <color name="tv_pip_menu_icon_disabled">#80868B</color>
+ <color name="tv_pip_menu_icon_bg_focused">#E8EAED</color>
+ <color name="tv_pip_menu_icon_bg_unfocused">#990E0E0F</color>
+ <color name="tv_pip_menu_focus_border">#E8EAED</color>
+</resources> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index af78293eb3ea..7a398c504546 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -213,6 +213,27 @@
+ compat_button_margin - compat_hint_corner_radius - compat_hint_point_width / 2). -->
<dimen name="compat_hint_padding_end">7dp</dimen>
+ <!-- The width of the size compat hint. -->
+ <dimen name="size_compat_hint_width">188dp</dimen>
+
+ <!-- The width of the camera compat hint. -->
+ <dimen name="camera_compat_hint_width">143dp</dimen>
+
+ <!-- The corner radius of the letterbox education dialog. -->
+ <dimen name="letterbox_education_dialog_corner_radius">28dp</dimen>
+
+ <!-- The size of an icon in the letterbox education dialog. -->
+ <dimen name="letterbox_education_dialog_icon_size">48dp</dimen>
+
+ <!-- The width of each action container in the letterbox education dialog -->
+ <dimen name="letterbox_education_dialog_action_width">140dp</dimen>
+
+ <!-- The space between two actions in the letterbox education dialog -->
+ <dimen name="letterbox_education_dialog_space_between_actions">24dp</dimen>
+
+ <!-- The maximum width of the title and subtitle in the letterbox education dialog. -->
+ <dimen name="letterbox_education_dialog_title_max_width">444dp</dimen>
+
<!-- The width of the brand image on staring surface. -->
<dimen name="starting_surface_brand_image_width">200dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index c88fc16e218e..a24311fb1f21 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -158,4 +158,32 @@
<!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
<string name="restart_button_description">Tap to restart this app and go full screen.</string>
+
+ <!-- Description of the camera compat button for applying stretched issues treatment in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_treatment_suggested_button_description">Camera issues?\nTap to refit</string>
+
+ <!-- Description of the camera compat button for reverting stretched issues treatment in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_treatment_applied_button_description">Didn\u2019t fix it?\nTap to revert</string>
+
+ <!-- Accessibillity description of the camera dismiss button for stretched issues in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_dismiss_button_description">No camera issues? Tap to dismiss.</string>
+
+ <!-- The title of the letterbox education dialog. [CHAR LIMIT=NONE] -->
+ <string name="letterbox_education_dialog_title">Some apps work best in portrait</string>
+
+ <!-- The subtext of the letterbox education dialog. [CHAR LIMIT=NONE] -->
+ <string name="letterbox_education_dialog_subtext">Try one of these options to make the most of your space</string>
+
+ <!-- Description of the rotate screen action. [CHAR LIMIT=NONE] -->
+ <string name="letterbox_education_screen_rotation_text">Rotate your device to go full screen</string>
+
+ <!-- Description of the reposition app action. [CHAR LIMIT=NONE] -->
+ <string name="letterbox_education_reposition_text">Double-tap next to an app to reposition it</string>
+
+ <!-- Button text for dismissing the letterbox education dialog. [CHAR LIMIT=20] -->
+ <string name="letterbox_education_got_it">Got it</string>
+
</resources>
diff --git a/libs/WindowManager/Shell/res/values/strings_tv.xml b/libs/WindowManager/Shell/res/values/strings_tv.xml
index 2dfdcabaa931..730d80894c41 100644
--- a/libs/WindowManager/Shell/res/values/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values/strings_tv.xml
@@ -30,5 +30,8 @@
<!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] -->
<string name="pip_fullscreen">Full screen</string>
+
+ <!-- Button to move picture-in-picture (PIP) via DPAD in the PIP menu [CHAR LIMIT=30] -->
+ <string name="pip_move">Move PIP</string>
</resources>
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 8b3a35688f11..91ea436f81b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -458,7 +458,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
newListener.onTaskInfoChanged(taskInfo);
}
notifyLocusVisibilityIfNeeded(taskInfo);
- if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) {
+ if (updated || !taskInfo.equalsForCompatUi(data.getTaskInfo())) {
// Notify the compat UI if the listener or task info changed.
notifyCompatUI(taskInfo, newListener);
}
@@ -607,6 +607,19 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token);
}
+ @Override
+ public void onCameraControlStateUpdated(
+ int taskId, @TaskInfo.CameraCompatControlState int state) {
+ final TaskAppearedInfo info;
+ synchronized (mLock) {
+ info = mTasks.get(taskId);
+ }
+ if (info == null) {
+ return;
+ }
+ updateCameraCompatControlState(info.getTaskInfo().token, state);
+ }
+
private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
int event) {
ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
@@ -633,14 +646,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
// The task is vanished or doesn't support compat UI, notify to remove compat UI
// on this Task if there is any.
if (taskListener == null || !taskListener.supportCompatUI()
- || !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) {
- mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
- null /* taskConfig */, null /* taskListener */);
+ || !taskInfo.hasCompatUI() || !taskInfo.isVisible) {
+ mCompatUI.onCompatInfoChanged(taskInfo, null /* taskListener */);
return;
}
-
- mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
- taskInfo.configuration, taskListener);
+ mCompatUI.onCompatInfoChanged(taskInfo, taskListener);
}
private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) {
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 2f3214d1d1ab..54e743f72cc6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -41,6 +41,7 @@ import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -77,6 +78,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private final ShellTaskOrganizer mTaskOrganizer;
private final Executor mShellExecutor;
private final SyncTransactionQueue mSyncQueue;
+ private final TaskViewTransitions mTaskViewTransitions;
private ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mTaskToken;
@@ -92,17 +94,27 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private final Rect mTmpRootRect = new Rect();
private final int[] mTmpLocation = new int[2];
- public TaskView(Context context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
+ public TaskView(Context context, ShellTaskOrganizer organizer,
+ TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
mTaskOrganizer = organizer;
mShellExecutor = organizer.getExecutor();
mSyncQueue = syncQueue;
+ mTaskViewTransitions = taskViewTransitions;
+ if (mTaskViewTransitions != null) {
+ mTaskViewTransitions.addTaskView(this);
+ }
setUseAlpha();
getHolder().addCallback(this);
mGuard.open("release");
}
+ /** Until all users are converted, we may have mixed-use (eg. Car). */
+ private boolean isUsingShellTransitions() {
+ return mTaskViewTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS;
+ }
+
/**
* Only one listener may be set on the view, throws an exception otherwise.
*/
@@ -129,6 +141,14 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
@NonNull ActivityOptions options, @Nullable Rect launchBounds) {
prepareActivityOptions(options, launchBounds);
LauncherApps service = mContext.getSystemService(LauncherApps.class);
+ if (isUsingShellTransitions()) {
+ mShellExecutor.execute(() -> {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.startShortcut(mContext.getPackageName(), shortcut, options.toBundle());
+ mTaskViewTransitions.startTaskView(wct, this);
+ });
+ return;
+ }
try {
service.startShortcut(shortcut, null /* sourceBounds */, options.toBundle());
} catch (Exception e) {
@@ -148,6 +168,14 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
@NonNull ActivityOptions options, @Nullable Rect launchBounds) {
prepareActivityOptions(options, launchBounds);
+ if (isUsingShellTransitions()) {
+ mShellExecutor.execute(() -> {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.sendPendingIntent(pendingIntent, fillInIntent, options.toBundle());
+ mTaskViewTransitions.startTaskView(wct, this);
+ });
+ return;
+ }
try {
pendingIntent.send(mContext, 0 /* code */, fillInIntent,
null /* onFinished */, null /* handler */, null /* requiredPermission */,
@@ -177,6 +205,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
mObscuredTouchRect = obscuredRect;
}
+ private void onLocationChanged(WindowContainerTransaction wct) {
+ // Update based on the screen bounds
+ getBoundsOnScreen(mTmpRect);
+ getRootView().getBoundsOnScreen(mTmpRootRect);
+ if (!mTmpRootRect.contains(mTmpRect)) {
+ mTmpRect.offsetTo(0, 0);
+ }
+ wct.setBounds(mTaskToken, mTmpRect);
+ }
+
/**
* Call when view position or size has changed. Do not call when animating.
*/
@@ -184,15 +222,12 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
if (mTaskToken == null) {
return;
}
- // Update based on the screen bounds
- getBoundsOnScreen(mTmpRect);
- getRootView().getBoundsOnScreen(mTmpRootRect);
- if (!mTmpRootRect.contains(mTmpRect)) {
- mTmpRect.offsetTo(0, 0);
- }
+ // Sync Transactions can't operate simultaneously with shell transition collection.
+ // The transition animation (upon showing) will sync the location itself.
+ if (isUsingShellTransitions() && mTaskViewTransitions.hasPending()) return;
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(mTaskToken, mTmpRect);
+ onLocationChanged(wct);
mSyncQueue.queue(wct);
}
@@ -217,6 +252,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private void performRelease() {
getHolder().removeCallback(this);
+ if (mTaskViewTransitions != null) {
+ mTaskViewTransitions.removeTaskView(this);
+ }
mShellExecutor.execute(() -> {
mTaskOrganizer.removeListener(this);
resetTaskInfo();
@@ -254,6 +292,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl leash) {
+ if (isUsingShellTransitions()) {
+ // Everything else handled by enter transition.
+ return;
+ }
mTaskInfo = taskInfo;
mTaskToken = taskInfo.token;
mTaskLeash = leash;
@@ -288,6 +330,8 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ // Unlike Appeared, we can't yet guarantee that vanish will happen within a transition that
+ // we know about -- so leave clean-up here even if shell transitions are enabled.
if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
if (mListener != null) {
@@ -355,6 +399,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
// Nothing to update, task is not yet available
return;
}
+ if (isUsingShellTransitions()) {
+ mTaskViewTransitions.setTaskViewVisible(this, true /* visible */);
+ return;
+ }
// Reparent the task when this surface is created
mTransaction.reparent(mTaskLeash, getSurfaceControl())
.show(mTaskLeash)
@@ -380,6 +428,11 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
return;
}
+ if (isUsingShellTransitions()) {
+ mTaskViewTransitions.setTaskViewVisible(this, false /* visible */);
+ return;
+ }
+
// Unparent the task when this surface is destroyed
mTransaction.reparent(mTaskLeash, null).apply();
updateTaskVisibility();
@@ -421,4 +474,91 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
super.onDetachedFromWindow();
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
}
+
+ ActivityManager.RunningTaskInfo getTaskInfo() {
+ return mTaskInfo;
+ }
+
+ void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) {
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+
+ finishTransaction.reparent(mTaskLeash, null).apply();
+
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
+ }
+ }
+
+ /**
+ * Called when the associated Task closes. If the TaskView is just being hidden, prepareHide
+ * is used instead.
+ */
+ void prepareCloseAnimation() {
+ if (mTaskToken != null) {
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ mListenerExecutor.execute(() -> {
+ mListener.onTaskRemovalStarted(taskId);
+ });
+ }
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+ }
+ resetTaskInfo();
+ }
+
+ void prepareOpenAnimation(final boolean newTask,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+ WindowContainerTransaction wct) {
+ mTaskInfo = taskInfo;
+ mTaskToken = mTaskInfo.token;
+ mTaskLeash = leash;
+ if (mSurfaceCreated) {
+ // Surface is ready, so just reparent the task to this surface control
+ startTransaction.reparent(mTaskLeash, getSurfaceControl())
+ .show(mTaskLeash)
+ .apply();
+ // Also reparent on finishTransaction since the finishTransaction will reparent back
+ // to its "original" parent by default.
+ finishTransaction.reparent(mTaskLeash, getSurfaceControl())
+ .setPosition(mTaskLeash, 0, 0)
+ .apply();
+
+ // TODO: determine if this is really necessary or not
+ onLocationChanged(wct);
+ } else {
+ // The surface has already been destroyed before the task has appeared,
+ // so go ahead and hide the task entirely
+ wct.setHidden(mTaskToken, true /* hidden */);
+ // listener callback is below
+ }
+ if (newTask) {
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true /* intercept */);
+ }
+
+ if (mTaskInfo.taskDescription != null) {
+ int backgroundColor = mTaskInfo.taskDescription.getBackgroundColor();
+ setResizeBackgroundColor(startTransaction, backgroundColor);
+ }
+
+ if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
+ final ComponentName baseActivity = mTaskInfo.baseActivity;
+
+ mListenerExecutor.execute(() -> {
+ if (newTask) {
+ mListener.onTaskCreated(taskId, baseActivity);
+ }
+ // Even if newTask, send a visibilityChange if the surface was destroyed.
+ if (!newTask || !mSurfaceCreated) {
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated /* visible */);
+ }
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
index 8286d102791e..42844b57b92a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -31,13 +31,24 @@ public class TaskViewFactoryController {
private final ShellTaskOrganizer mTaskOrganizer;
private final ShellExecutor mShellExecutor;
private final SyncTransactionQueue mSyncQueue;
+ private final TaskViewTransitions mTaskViewTransitions;
private final TaskViewFactory mImpl = new TaskViewFactoryImpl();
public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
+ ShellExecutor shellExecutor, SyncTransactionQueue syncQueue,
+ TaskViewTransitions taskViewTransitions) {
+ mTaskOrganizer = taskOrganizer;
+ mShellExecutor = shellExecutor;
+ mSyncQueue = syncQueue;
+ mTaskViewTransitions = taskViewTransitions;
+ }
+
+ public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
ShellExecutor shellExecutor, SyncTransactionQueue syncQueue) {
mTaskOrganizer = taskOrganizer;
mShellExecutor = shellExecutor;
mSyncQueue = syncQueue;
+ mTaskViewTransitions = null;
}
public TaskViewFactory asTaskViewFactory() {
@@ -46,7 +57,7 @@ public class TaskViewFactoryController {
/** Creates an {@link TaskView} */
public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
- TaskView taskView = new TaskView(context, mTaskOrganizer, mSyncQueue);
+ TaskView taskView = new TaskView(context, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
executor.execute(() -> {
onCreate.accept(taskView);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
new file mode 100644
index 000000000000..83335ac24799
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewTransitions.java
@@ -0,0 +1,253 @@
+/*
+ * 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;
+
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.ArrayList;
+
+/**
+ * Handles Shell Transitions that involve TaskView tasks.
+ */
+public class TaskViewTransitions implements Transitions.TransitionHandler {
+ private static final String TAG = "TaskViewTransitions";
+
+ private final ArrayList<TaskView> mTaskViews = new ArrayList<>();
+ private final ArrayList<PendingTransition> mPending = new ArrayList<>();
+ private final Transitions mTransitions;
+ private final boolean[] mRegistered = new boolean[]{ false };
+
+ /**
+ * TaskView makes heavy use of startTransition. Only one shell-initiated transition can be
+ * in-flight (collecting) at a time (because otherwise, the operations could get merged into
+ * a single transition). So, keep a queue here until we add a queue in server-side.
+ */
+ private static class PendingTransition {
+ final @WindowManager.TransitionType int mType;
+ final WindowContainerTransaction mWct;
+ final @NonNull TaskView mTaskView;
+ IBinder mClaimed;
+
+ PendingTransition(@WindowManager.TransitionType int type,
+ @Nullable WindowContainerTransaction wct, @NonNull TaskView taskView) {
+ mType = type;
+ mWct = wct;
+ mTaskView = taskView;
+ }
+ }
+
+ public TaskViewTransitions(Transitions transitions) {
+ mTransitions = transitions;
+ // Defer registration until the first TaskView because we want this to be the "first" in
+ // priority when handling requests.
+ // TODO(210041388): register here once we have an explicit ordering mechanism.
+ }
+
+ void addTaskView(TaskView tv) {
+ synchronized (mRegistered) {
+ if (!mRegistered[0]) {
+ mRegistered[0] = true;
+ mTransitions.addHandler(this);
+ }
+ }
+ mTaskViews.add(tv);
+ }
+
+ void removeTaskView(TaskView tv) {
+ mTaskViews.remove(tv);
+ // Note: Don't unregister handler since this is a singleton with lifetime bound to Shell
+ }
+
+ /**
+ * Looks through the pending transitions for one matching `taskView`.
+ * @param taskView the pending transition should be for this.
+ * @param closing When true, this only returns a pending transition of the close/hide type.
+ * Otherwise it selects open/show.
+ * @param latest When true, this will only check the most-recent pending transition for the
+ * specified taskView. If it doesn't match `closing`, this will return null even
+ * if there is a match earlier. The idea behind this is to check the state of
+ * the taskviews "as if all transitions already happened".
+ */
+ private PendingTransition findPending(TaskView taskView, boolean closing, boolean latest) {
+ for (int i = mPending.size() - 1; i >= 0; --i) {
+ if (mPending.get(i).mTaskView != taskView) continue;
+ if (Transitions.isClosingType(mPending.get(i).mType) == closing) {
+ return mPending.get(i);
+ }
+ if (latest) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ private PendingTransition findPending(IBinder claimed) {
+ for (int i = 0; i < mPending.size(); ++i) {
+ if (mPending.get(i).mClaimed != claimed) continue;
+ return mPending.get(i);
+ }
+ return null;
+ }
+
+ /** @return whether there are pending transitions on TaskViews. */
+ public boolean hasPending() {
+ return !mPending.isEmpty();
+ }
+
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @Nullable TransitionRequestInfo request) {
+ final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
+ if (triggerTask == null) {
+ return null;
+ }
+ final TaskView taskView = findTaskView(triggerTask);
+ if (taskView == null) return null;
+ // Opening types should all be initiated by shell
+ if (!Transitions.isClosingType(request.getType())) return null;
+ PendingTransition pending = findPending(taskView, true /* closing */, false /* latest */);
+ if (pending == null) {
+ pending = new PendingTransition(request.getType(), null, taskView);
+ }
+ if (pending.mClaimed != null) {
+ throw new IllegalStateException("Task is closing in 2 collecting transitions?"
+ + " This state doesn't make sense");
+ }
+ pending.mClaimed = transition;
+ return new WindowContainerTransaction();
+ }
+
+ private TaskView findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
+ for (int i = 0; i < mTaskViews.size(); ++i) {
+ if (mTaskViews.get(i).getTaskInfo() == null) continue;
+ if (taskInfo.token.equals(mTaskViews.get(i).getTaskInfo().token)) {
+ return mTaskViews.get(i);
+ }
+ }
+ return null;
+ }
+
+ void startTaskView(WindowContainerTransaction wct, TaskView taskView) {
+ mPending.add(new PendingTransition(TRANSIT_OPEN, wct, taskView));
+ startNextTransition();
+ }
+
+ void setTaskViewVisible(TaskView taskView, boolean visible) {
+ PendingTransition pending = findPending(taskView, !visible, true /* latest */);
+ if (pending != null) {
+ // Already opening or creating a task, so no need to do anything here.
+ return;
+ }
+ if (taskView.getTaskInfo() == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setHidden(taskView.getTaskInfo().token, !visible /* hidden */);
+ pending = new PendingTransition(
+ visible ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK, wct, taskView);
+ mPending.add(pending);
+ startNextTransition();
+ // visibility is reported in transition.
+ }
+
+ private void startNextTransition() {
+ if (mPending.isEmpty()) return;
+ final PendingTransition pending = mPending.get(0);
+ if (pending.mClaimed != null) {
+ // Wait for this to start animating.
+ return;
+ }
+ pending.mClaimed = mTransitions.startTransition(pending.mType, pending.mWct, this);
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ PendingTransition pending = findPending(transition);
+ if (pending == null) return false;
+ mPending.remove(pending);
+ TaskView taskView = pending.mTaskView;
+ final ArrayList<TransitionInfo.Change> tasks = new ArrayList<>();
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change chg = info.getChanges().get(i);
+ if (chg.getTaskInfo() == null) continue;
+ tasks.add(chg);
+ }
+ if (tasks.isEmpty()) {
+ Slog.e(TAG, "Got a TaskView transition with no task.");
+ return false;
+ }
+ WindowContainerTransaction wct = null;
+ for (int i = 0; i < tasks.size(); ++i) {
+ TransitionInfo.Change chg = tasks.get(i);
+ if (Transitions.isClosingType(chg.getMode())) {
+ final boolean isHide = chg.getMode() == TRANSIT_TO_BACK;
+ TaskView tv = findTaskView(chg.getTaskInfo());
+ if (tv == null) {
+ throw new IllegalStateException("TaskView transition is closing a "
+ + "non-taskview task ");
+ }
+ if (isHide) {
+ tv.prepareHideAnimation(finishTransaction);
+ } else {
+ tv.prepareCloseAnimation();
+ }
+ } else if (Transitions.isOpeningType(chg.getMode())) {
+ final boolean taskIsNew = chg.getMode() == TRANSIT_OPEN;
+ if (wct == null) wct = new WindowContainerTransaction();
+ TaskView tv = taskView;
+ if (!taskIsNew) {
+ tv = findTaskView(chg.getTaskInfo());
+ if (tv == null) {
+ throw new IllegalStateException("TaskView transition is showing a "
+ + "non-taskview task ");
+ }
+ }
+ tv.prepareOpenAnimation(taskIsNew, startTransaction, finishTransaction,
+ chg.getTaskInfo(), chg.getLeash(), wct);
+ } else {
+ throw new IllegalStateException("Claimed transition isn't an opening or closing"
+ + " type: " + chg.getMode());
+ }
+ }
+ // No animation, just show it immediately.
+ startTransaction.apply();
+ finishTransaction.apply();
+ finishCallback.onTransitionFinished(wct, null /* wctCB */);
+ startNextTransition();
+ return true;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
new file mode 100644
index 000000000000..9a6df23ca971
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -0,0 +1,45 @@
+/*
+ * 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.back;
+
+import android.view.MotionEvent;
+
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+/**
+ * Interface for external process to get access to the Back animation related methods.
+ */
+@ExternalThread
+public interface BackAnimation {
+
+ /**
+ * Called when a {@link MotionEvent} is generated by a back gesture.
+ */
+ void onBackMotion(MotionEvent event);
+
+ /**
+ * Sets whether the back gesture is past the trigger threshold or not.
+ */
+ void setTriggerBack(boolean triggerBack);
+
+ /**
+ * Returns a binder that can be passed to an external process to update back animations.
+ */
+ default IBackAnimation createExternalInterface() {
+ return 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
new file mode 100644
index 000000000000..a5140c3aafff
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -0,0 +1,360 @@
+/*
+ * 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.back;
+
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+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.annotation.Nullable;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+import android.window.IOnBackInvokedCallback;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.RemoteCallable;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
+/**
+ * Controls the window animation run when a user initiates a back gesture.
+ */
+public class BackAnimationController implements RemoteCallable<BackAnimationController> {
+
+ private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+ public static final boolean IS_ENABLED = SystemProperties
+ .getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+ private static final String TAG = "BackAnimationController";
+
+ /**
+ * Location of the initial touch event of the back gesture.
+ */
+ private final PointF mInitTouchLocation = new PointF();
+
+ /**
+ * Raw delta between {@link #mInitTouchLocation} and the last touch location.
+ */
+ private final Point mTouchEventDelta = new Point();
+ private final ShellExecutor mShellExecutor;
+
+ /** True when a back gesture is ongoing */
+ private boolean mBackGestureStarted = false;
+
+ /** @see #setTriggerBack(boolean) */
+ private boolean mTriggerBack;
+
+ @Nullable
+ private BackNavigationInfo mBackNavigationInfo;
+ private final SurfaceControl.Transaction mTransaction;
+ private final IActivityTaskManager mActivityTaskManager;
+ private final Context mContext;
+ @Nullable
+ private IOnBackInvokedCallback mBackToLauncherCallback;
+
+ public BackAnimationController(
+ @ShellMainThread ShellExecutor shellExecutor,
+ Context context) {
+ this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService(),
+ context);
+ }
+
+ @VisibleForTesting
+ BackAnimationController(@NonNull ShellExecutor shellExecutor,
+ @NonNull SurfaceControl.Transaction transaction,
+ @NonNull IActivityTaskManager activityTaskManager,
+ Context context) {
+ mShellExecutor = shellExecutor;
+ mTransaction = transaction;
+ mActivityTaskManager = activityTaskManager;
+ mContext = context;
+ }
+
+ public BackAnimation getBackAnimationImpl() {
+ return mBackAnimation;
+ }
+
+ private final BackAnimation mBackAnimation = new BackAnimationImpl();
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mShellExecutor;
+ }
+
+ private class BackAnimationImpl implements BackAnimation {
+ private IBackAnimationImpl mBackAnimation;
+
+ @Override
+ public IBackAnimation createExternalInterface() {
+ if (mBackAnimation != null) {
+ mBackAnimation.invalidate();
+ }
+ mBackAnimation = new IBackAnimationImpl(BackAnimationController.this);
+ return mBackAnimation;
+ }
+
+ @Override
+ public void onBackMotion(MotionEvent event) {
+ mShellExecutor.execute(() -> onMotionEvent(event));
+ }
+
+ @Override
+ public void setTriggerBack(boolean triggerBack) {
+ mShellExecutor.execute(() -> BackAnimationController.this.setTriggerBack(triggerBack));
+ }
+ }
+
+ private static class IBackAnimationImpl extends IBackAnimation.Stub {
+ private BackAnimationController mController;
+
+ IBackAnimationImpl(BackAnimationController controller) {
+ mController = controller;
+ }
+
+ @Override
+ public void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
+ executeRemoteCallWithTaskPermission(mController, "setBackToLauncherCallback",
+ (controller) -> mController.setBackToLauncherCallback(callback));
+ }
+
+ @Override
+ public void clearBackToLauncherCallback() {
+ executeRemoteCallWithTaskPermission(mController, "clearBackToLauncherCallback",
+ (controller) -> mController.clearBackToLauncherCallback());
+ }
+
+ @Override
+ public void onBackToLauncherAnimationFinished() {
+ executeRemoteCallWithTaskPermission(mController, "onBackToLauncherAnimationFinished",
+ (controller) -> mController.onBackToLauncherAnimationFinished());
+ }
+
+ void invalidate() {
+ mController = null;
+ }
+ }
+
+ private void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
+ mBackToLauncherCallback = callback;
+ }
+
+ private void clearBackToLauncherCallback() {
+ mBackToLauncherCallback = null;
+ }
+
+ private void onBackToLauncherAnimationFinished() {
+ finishAnimation();
+ }
+
+ /**
+ * Called when a new motion event needs to be transferred to this
+ * {@link BackAnimationController}
+ */
+ public void onMotionEvent(MotionEvent event) {
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ initAnimation(event);
+ } else if (action == MotionEvent.ACTION_MOVE) {
+ onMove(event);
+ } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ onGestureFinished();
+ }
+ }
+
+ private void initAnimation(MotionEvent event) {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "initAnimation mMotionStarted=%b", mBackGestureStarted);
+ if (mBackGestureStarted) {
+ Log.e(TAG, "Animation is being initialized but is already started.");
+ return;
+ }
+
+ if (mBackNavigationInfo != null) {
+ finishAnimation();
+ }
+ mInitTouchLocation.set(event.getX(), event.getY());
+ mBackGestureStarted = true;
+
+ try {
+ mBackNavigationInfo = mActivityTaskManager.startBackNavigation();
+ onBackNavigationInfoReceived(mBackNavigationInfo);
+ } catch (RemoteException remoteException) {
+ Log.e(TAG, "Failed to initAnimation", remoteException);
+ finishAnimation();
+ }
+ }
+
+ private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo) {
+ if (backNavigationInfo == null
+ || backNavigationInfo.getDepartingWindowContainer() == null) {
+ Log.e(TAG, "Received BackNavigationInfo is null.");
+ finishAnimation();
+ return;
+ }
+
+ HardwareBuffer hardwareBuffer = backNavigationInfo.getScreenshotHardwareBuffer();
+ if (hardwareBuffer != null) {
+ displayTargetScreenshot(hardwareBuffer,
+ backNavigationInfo.getTaskWindowConfiguration());
+ }
+ mTransaction.apply();
+ }
+
+ /**
+ * 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. ");
+ 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(MotionEvent event) {
+ if (!mBackGestureStarted || mBackNavigationInfo == null) {
+ return;
+ }
+ int deltaX = Math.round(event.getX() - mInitTouchLocation.x);
+ int deltaY = Math.round(event.getY() - mInitTouchLocation.y);
+ ProtoLog.v(WM_SHELL_BACK_PREVIEW, "Runner move: %d %d", deltaX, deltaY);
+ SurfaceControl topWindowLeash = mBackNavigationInfo.getDepartingWindowContainer();
+ mTransaction.setPosition(topWindowLeash, deltaX, deltaY);
+ mTouchEventDelta.set(deltaX, deltaY);
+ mTransaction.apply();
+ }
+
+ private void onGestureFinished() {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
+ if (mBackGestureStarted) {
+ if (mTriggerBack) {
+ prepareTransition();
+ } else {
+ resetPositionAnimated();
+ }
+ }
+ mBackGestureStarted = false;
+ mTriggerBack = false;
+ }
+
+ /**
+ * Animate the top window leash to its initial position.
+ */
+ private void resetPositionAnimated() {
+ mBackGestureStarted = false;
+ // TODO(208786853) Handle overlap with a new coming gesture.
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Runner: Back not triggered, cancelling animation "
+ + "mLastPos=%s mInitTouch=%s", mTouchEventDelta, mInitTouchLocation);
+
+ // TODO(208427216) : Replace placeholder animation with an actual one.
+ ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f).setDuration(200);
+ animation.addUpdateListener(animation1 -> {
+ if (mBackNavigationInfo == null) {
+ return;
+ }
+ float fraction = animation1.getAnimatedFraction();
+ int deltaX = Math.round(mTouchEventDelta.x - (mTouchEventDelta.x * fraction));
+ int deltaY = Math.round(mTouchEventDelta.y - (mTouchEventDelta.y * fraction));
+ mTransaction.setPosition(mBackNavigationInfo.getDepartingWindowContainer(),
+ deltaX, deltaY);
+ mTransaction.apply();
+ });
+
+ animation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onAnimationEnd");
+ finishAnimation();
+ }
+ });
+ animation.start();
+ }
+
+ private void prepareTransition() {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "prepareTransition()");
+ mTriggerBack = false;
+ mBackGestureStarted = false;
+ }
+
+ /**
+ * Sets to true when the back gesture has passed the triggering threshold, false otherwise.
+ */
+ public void setTriggerBack(boolean triggerBack) {
+ mTriggerBack = triggerBack;
+ }
+
+ private void finishAnimation() {
+ ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishAnimation()");
+ mBackGestureStarted = false;
+ mTouchEventDelta.set(0, 0);
+ mInitTouchLocation.set(0, 0);
+ BackNavigationInfo backNavigationInfo = mBackNavigationInfo;
+ mBackNavigationInfo = null;
+ if (backNavigationInfo == null) {
+ return;
+ }
+ SurfaceControl topWindowLeash = backNavigationInfo.getDepartingWindowContainer();
+ if (topWindowLeash != null && topWindowLeash.isValid()) {
+ mTransaction.remove(topWindowLeash);
+ }
+ SurfaceControl screenshotSurface = backNavigationInfo.getScreenshotSurface();
+ if (screenshotSurface != null && screenshotSurface.isValid()) {
+ mTransaction.remove(screenshotSurface);
+ }
+ mTransaction.apply();
+ backNavigationInfo.onBackNavigationFinished();
+ }
+}
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
new file mode 100644
index 000000000000..6311f879fd45
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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 android.window.IOnBackInvokedCallback;
+
+/**
+ * Interface for Launcher process to register back invocation callbacks.
+ */
+interface IBackAnimation {
+
+ /**
+ * Sets a {@link IOnBackInvokedCallback} to be invoked when
+ * back navigation has type {@link BackNavigationInfo#TYPE_RETURN_TO_HOME}.
+ */
+ void setBackToLauncherCallback(in IOnBackInvokedCallback callback);
+
+ /**
+ * 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/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index d92e2ccc77bd..c52d87dde07f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -15,27 +15,27 @@
*/
package com.android.wm.shell.bubbles;
-import static android.graphics.Paint.ANTI_ALIAS_FLAG;
-import static android.graphics.Paint.DITHER_FLAG;
-import static android.graphics.Paint.FILTER_BITMAP_FLAG;
-
+import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.content.Context;
-import android.graphics.Bitmap;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.PathParser;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
+import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import java.util.EnumSet;
@@ -47,14 +47,12 @@ import java.util.EnumSet;
* Badge = the icon associated with the app that created this bubble, this will show work profile
* badge if appropriate.
*/
-public class BadgedImageView extends ImageView {
+public class BadgedImageView extends ConstraintLayout {
/** Same value as Launcher3 dot code */
public static final float WHITE_SCRIM_ALPHA = 0.54f;
/** Same as value in Launcher3 IconShape */
public static final int DEFAULT_PATH_SIZE = 100;
- /** Same as value in Launcher3 BaseIconFactory */
- private static final float ICON_BADGE_SCALE = 0.444f;
/**
* Flags that suppress the visibility of the 'new' dot, for one reason or another. If any of
@@ -74,6 +72,9 @@ public class BadgedImageView extends ImageView {
private final EnumSet<SuppressionFlag> mDotSuppressionFlags =
EnumSet.of(SuppressionFlag.FLYOUT_VISIBLE);
+ private final ImageView mBubbleIcon;
+ private final ImageView mAppIcon;
+
private float mDotScale = 0f;
private float mAnimatingToDotScale = 0f;
private boolean mDotIsAnimating = false;
@@ -86,7 +87,6 @@ public class BadgedImageView extends ImageView {
private DotRenderer.DrawParams mDrawParams;
private int mDotColor;
- private Paint mPaint = new Paint(ANTI_ALIAS_FLAG);
private Rect mTempBounds = new Rect();
public BadgedImageView(Context context) {
@@ -104,6 +104,19 @@ public class BadgedImageView extends ImageView {
public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ // We manage positioning the badge ourselves
+ setLayoutDirection(LAYOUT_DIRECTION_LTR);
+
+ LayoutInflater.from(context).inflate(R.layout.badged_image_view, this);
+
+ mBubbleIcon = findViewById(R.id.icon_view);
+ mAppIcon = findViewById(R.id.app_icon_view);
+
+ final TypedArray ta = mContext.obtainStyledAttributes(attrs, new int[]{android.R.attr.src},
+ defStyleAttr, defStyleRes);
+ mBubbleIcon.setImageResource(ta.getResourceId(0, 0));
+ ta.recycle();
+
mDrawParams = new DotRenderer.DrawParams();
setFocusable(true);
@@ -135,7 +148,6 @@ public class BadgedImageView extends ImageView {
public void showDotAndBadge(boolean onLeft) {
removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK);
animateDotBadgePositions(onLeft);
-
}
public void hideDotAndBadge(boolean onLeft) {
@@ -149,6 +161,8 @@ public class BadgedImageView extends ImageView {
*/
public void setRenderedBubble(BubbleViewProvider bubble) {
mBubble = bubble;
+ mBubbleIcon.setImageBitmap(bubble.getBubbleIcon());
+ mAppIcon.setImageBitmap(bubble.getAppBadge());
if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) {
hideBadge();
} else {
@@ -176,6 +190,20 @@ public class BadgedImageView extends ImageView {
mDotRenderer.draw(canvas, mDrawParams);
}
+ /**
+ * Set drawable resource shown as the icon
+ */
+ public void setIconImageResource(@DrawableRes int drawable) {
+ mBubbleIcon.setImageResource(drawable);
+ }
+
+ /**
+ * Get icon drawable
+ */
+ public Drawable getIconDrawable() {
+ return mBubbleIcon.getDrawable();
+ }
+
/** Adds a dot suppression flag, updating dot visibility if needed. */
void addDotSuppressionFlag(SuppressionFlag flag) {
if (mDotSuppressionFlags.add(flag)) {
@@ -279,7 +307,6 @@ public class BadgedImageView extends ImageView {
showBadge();
}
-
/** Whether to draw the dot in onDraw(). */
private boolean shouldDrawDot() {
// Always render the dot if it's animating, since it could be animating out. Otherwise, show
@@ -323,31 +350,21 @@ public class BadgedImageView extends ImageView {
}
void showBadge() {
- Bitmap badge = mBubble.getAppBadge();
- if (badge == null) {
- setImageBitmap(mBubble.getBubbleIcon());
+ if (mBubble.getAppBadge() == null) {
+ mAppIcon.setVisibility(GONE);
return;
}
- Canvas bubbleCanvas = new Canvas();
- Bitmap noBadgeBubble = mBubble.getBubbleIcon();
- Bitmap bubble = noBadgeBubble.copy(noBadgeBubble.getConfig(), /* isMutable */ true);
-
- bubbleCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
- bubbleCanvas.setBitmap(bubble);
- final int bubbleSize = bubble.getWidth();
- final int badgeSize = (int) (ICON_BADGE_SCALE * bubbleSize);
- Rect dest = new Rect();
+ int translationX;
if (mOnLeft) {
- dest.set(0, bubbleSize - badgeSize, badgeSize, bubbleSize);
+ translationX = -(mBubbleIcon.getWidth() - mAppIcon.getWidth());
} else {
- dest.set(bubbleSize - badgeSize, bubbleSize - badgeSize, bubbleSize, bubbleSize);
+ translationX = 0;
}
- bubbleCanvas.drawBitmap(badge, null /* src */, dest, mPaint);
- bubbleCanvas.setBitmap(null);
- setImageBitmap(bubble);
+ mAppIcon.setTranslationX(translationX);
+ mAppIcon.setVisibility(VISIBLE);
}
void hideBadge() {
- setImageBitmap(mBubble.getBubbleIcon());
+ mAppIcon.setVisibility(GONE);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 8d43f1375a8c..cf4647a09a58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -108,6 +108,8 @@ public class Bubble implements BubbleViewProvider {
private Bitmap mBubbleBitmap;
// The app badge for the bubble
private Bitmap mBadgeBitmap;
+ // App badge without any markings for important conversations
+ private Bitmap mRawBadgeBitmap;
private int mDotColor;
private Path mDotPath;
private int mFlags;
@@ -248,6 +250,11 @@ public class Bubble implements BubbleViewProvider {
}
@Override
+ public Bitmap getRawAppBadge() {
+ return mRawBadgeBitmap;
+ }
+
+ @Override
public int getDotColor() {
return mDotColor;
}
@@ -357,13 +364,15 @@ public class Bubble implements BubbleViewProvider {
* @param context the context for the bubble.
* @param controller the bubble controller.
* @param stackView the stackView the bubble is eventually added to.
- * @param iconFactory the iconfactory use to create badged images for the bubble.
+ * @param iconFactory the icon factory use to create images for the bubble.
+ * @param badgeIconFactory the icon factory to create app badges for the bubble.
*/
void inflate(BubbleViewInfoTask.Callback callback,
Context context,
BubbleController controller,
BubbleStackView stackView,
BubbleIconFactory iconFactory,
+ BubbleBadgeIconFactory badgeIconFactory,
boolean skipInflation) {
if (isBubbleLoading()) {
mInflationTask.cancel(true /* mayInterruptIfRunning */);
@@ -373,6 +382,7 @@ public class Bubble implements BubbleViewProvider {
controller,
stackView,
iconFactory,
+ badgeIconFactory,
skipInflation,
callback,
mMainExecutor);
@@ -409,6 +419,7 @@ public class Bubble implements BubbleViewProvider {
mFlyoutMessage = info.flyoutMessage;
mBadgeBitmap = info.badgeBitmap;
+ mRawBadgeBitmap = info.mRawBadgeBitmap;
mBubbleBitmap = info.bubbleBitmap;
mDotColor = info.dotColor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
new file mode 100644
index 000000000000..4eeb20769e09
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java
@@ -0,0 +1,121 @@
+/*
+ * 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.bubbles;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+
+import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ShadowGenerator;
+import com.android.wm.shell.R;
+
+/**
+ * Factory for creating app badge icons that are shown on bubbles.
+ */
+public class BubbleBadgeIconFactory extends BaseIconFactory {
+
+ public BubbleBadgeIconFactory(Context context) {
+ super(context, context.getResources().getConfiguration().densityDpi,
+ context.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size));
+ }
+
+ /**
+ * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This
+ * will include the workprofile indicator on the badge if appropriate.
+ */
+ BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
+ ShadowGenerator shadowGenerator = new ShadowGenerator(mIconBitmapSize);
+ Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mIconBitmapSize);
+
+ if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
+ userBadgedBitmap = Bitmap.createScaledBitmap(
+ getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
+ userBadgedAppIcon.getIntrinsicWidth()),
+ mIconBitmapSize, mIconBitmapSize, /* filter */ true);
+ }
+
+ if (isImportantConversation) {
+ final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.importance_ring_stroke_width);
+ final int importantConversationColor = mContext.getResources().getColor(
+ R.color.important_conversation, null);
+ Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
+ userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
+ Canvas c = new Canvas(badgeAndRing);
+
+ Paint ringPaint = new Paint();
+ ringPaint.setStyle(Paint.Style.FILL);
+ ringPaint.setColor(importantConversationColor);
+ ringPaint.setAntiAlias(true);
+ c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint);
+
+ final int bitmapTop = (int) ringStrokeWidth;
+ final int bitmapLeft = (int) ringStrokeWidth;
+ final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
+ final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
+
+ Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
+ bitmapHeight, /* filter */ true);
+ c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
+
+ shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
+ return createIconBitmap(badgeAndRing);
+ } else {
+ Canvas c = new Canvas();
+ c.setBitmap(userBadgedBitmap);
+ shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
+ return createIconBitmap(userBadgedBitmap);
+ }
+ }
+
+ private Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
+ Drawable foreground = icon.getForeground();
+ Drawable background = icon.getBackground();
+ Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas();
+ canvas.setBitmap(bitmap);
+
+ // Clip canvas to circle.
+ Path circlePath = new Path();
+ circlePath.addCircle(/* x */ size / 2f,
+ /* y */ size / 2f,
+ /* radius */ size / 2f,
+ Path.Direction.CW);
+ canvas.clipPath(circlePath);
+
+ // Draw background.
+ background.setBounds(0, 0, size, size);
+ background.draw(canvas);
+
+ // Draw foreground. The foreground and background drawables are derived from adaptive icons
+ // Some icon shapes fill more space than others, so adaptive icons are normalized to about
+ // the same size. This size is smaller than the original bounds, so we estimate
+ // the difference in this offset.
+ int offset = size / 5;
+ foreground.setBounds(-offset, -offset, size + offset, size + offset);
+ foreground.draw(canvas);
+
+ canvas.setBitmap(null);
+ return bitmap;
+ }
+}
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 ec59fad3e95b..57cb7a5a57d7 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
@@ -17,6 +17,8 @@
package com.android.wm.shell.bubbles;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
@@ -42,6 +44,7 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -80,6 +83,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
@@ -88,6 +92,8 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import java.io.FileDescriptor;
@@ -97,6 +103,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -136,6 +143,7 @@ public class BubbleController {
private final TaskStackListenerImpl mTaskStackListener;
private final ShellTaskOrganizer mTaskOrganizer;
private final DisplayController mDisplayController;
+ private final TaskViewTransitions mTaskViewTransitions;
private final SyncTransactionQueue mSyncQueue;
// Used to post to main UI thread
@@ -146,6 +154,7 @@ public class BubbleController {
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
private BubbleIconFactory mBubbleIconFactory;
+ private BubbleBadgeIconFactory mBubbleBadgeIconFactory;
private BubblePositioner mBubblePositioner;
private Bubbles.SysuiProxy mSysuiProxy;
@@ -196,6 +205,9 @@ public class BubbleController {
/** True when user is in status bar unlock shade. */
private boolean mIsStatusBarShade = true;
+ /** One handed mode controller to register transition listener. */
+ private Optional<OneHandedController> mOneHandedOptional;
+
/**
* Creates an instance of the BubbleController.
*/
@@ -210,8 +222,10 @@ public class BubbleController {
UiEventLogger uiEventLogger,
ShellTaskOrganizer organizer,
DisplayController displayController,
+ Optional<OneHandedController> oneHandedOptional,
ShellExecutor mainExecutor,
Handler mainHandler,
+ TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
BubblePositioner positioner = new BubblePositioner(context, windowManager);
@@ -219,8 +233,9 @@ public class BubbleController {
return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- logger, taskStackListener, organizer, positioner, displayController, mainExecutor,
- mainHandler, syncQueue);
+ logger, taskStackListener, organizer, positioner, displayController,
+ oneHandedOptional, mainExecutor, mainHandler, taskViewTransitions,
+ syncQueue);
}
/**
@@ -241,8 +256,10 @@ public class BubbleController {
ShellTaskOrganizer organizer,
BubblePositioner positioner,
DisplayController displayController,
+ Optional<OneHandedController> oneHandedOptional,
ShellExecutor mainExecutor,
Handler mainHandler,
+ TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
mContext = context;
mLauncherApps = launcherApps;
@@ -265,10 +282,32 @@ public class BubbleController {
mBubbleData = data;
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mBubbleIconFactory = new BubbleIconFactory(context);
+ mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(context);
mDisplayController = displayController;
+ mTaskViewTransitions = taskViewTransitions;
+ mOneHandedOptional = oneHandedOptional;
mSyncQueue = syncQueue;
}
+ private void registerOneHandedState(OneHandedController oneHanded) {
+ oneHanded.registerTransitionCallback(
+ new OneHandedTransitionCallback() {
+ @Override
+ public void onStartFinished(Rect bounds) {
+ if (mStackView != null) {
+ mStackView.onVerticalOffsetChanged(bounds.top);
+ }
+ }
+
+ @Override
+ public void onStopFinished(Rect bounds) {
+ if (mStackView != null) {
+ mStackView.onVerticalOffsetChanged(bounds.top);
+ }
+ }
+ });
+ }
+
public void initialize() {
mBubbleData.setListener(mBubbleDataListener);
mBubbleData.setSuppressionChangedListener(this::onBubbleNotificationSuppressionChanged);
@@ -392,6 +431,8 @@ public class BubbleController {
}
}
});
+
+ mOneHandedOptional.ifPresent(this::registerOneHandedState);
}
@VisibleForTesting
@@ -464,6 +505,7 @@ public class BubbleController {
}
mStackView.updateStackPosition();
mBubbleIconFactory = new BubbleIconFactory(mContext);
+ mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext);
mStackView.onDisplaySizeChanged();
}
if (b.getBoolean(EXTRA_BUBBLE_OVERFLOW_OPENED, false)) {
@@ -570,6 +612,10 @@ public class BubbleController {
return mSyncQueue;
}
+ TaskViewTransitions getTaskViewTransitions() {
+ return mTaskViewTransitions;
+ }
+
/** Contains information to help position things on the screen. */
BubblePositioner getPositioner() {
return mBubblePositioner;
@@ -738,13 +784,17 @@ public class BubbleController {
mStackView.onThemeChanged();
}
mBubbleIconFactory = new BubbleIconFactory(mContext);
+ mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext);
+
// Reload each bubble
- for (Bubble b: mBubbleData.getBubbles()) {
+ for (Bubble b : mBubbleData.getBubbles()) {
b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory,
+ mBubbleBadgeIconFactory,
false /* skipInflation */);
}
- for (Bubble b: mBubbleData.getOverflowBubbles()) {
+ for (Bubble b : mBubbleData.getOverflowBubbles()) {
b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory,
+ mBubbleBadgeIconFactory,
false /* skipInflation */);
}
}
@@ -760,6 +810,7 @@ public class BubbleController {
mScreenBounds.set(newConfig.windowConfiguration.getBounds());
mBubbleData.onMaxBubblesChanged();
mBubbleIconFactory = new BubbleIconFactory(mContext);
+ mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext);
mStackView.onDisplaySizeChanged();
}
if (newConfig.fontScale != mFontScale) {
@@ -921,7 +972,8 @@ public class BubbleController {
}
bubble.inflate(
(b) -> mBubbleData.overflowBubble(Bubbles.DISMISS_RELOAD_FROM_DISK, bubble),
- mContext, this, mStackView, mBubbleIconFactory, true /* skipInflation */);
+ mContext, this, mStackView, mBubbleIconFactory, mBubbleBadgeIconFactory,
+ true /* skipInflation */);
});
return null;
});
@@ -956,7 +1008,8 @@ public class BubbleController {
ensureStackViewCreated();
bubble.setInflateSynchronously(mInflateSynchronously);
bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
- mContext, this, mStackView, mBubbleIconFactory, false /* skipInflation */);
+ mContext, this, mStackView, mBubbleIconFactory, mBubbleBadgeIconFactory,
+ false /* skipInflation */);
}
/**
@@ -1042,6 +1095,24 @@ public class BubbleController {
}
}
+ @VisibleForTesting
+ public void onNotificationChannelModified(String pkg, UserHandle user,
+ NotificationChannel channel, int modificationType) {
+ // Only query overflow bubbles here because active bubbles will have an active notification
+ // and channel changes we care about would result in a ranking update.
+ List<Bubble> overflowBubbles = new ArrayList<>(mBubbleData.getOverflowBubbles());
+ for (int i = 0; i < overflowBubbles.size(); i++) {
+ Bubble b = overflowBubbles.get(i);
+ if (Objects.equals(b.getShortcutId(), channel.getConversationId())
+ && b.getPackageName().equals(pkg)
+ && b.getUser().getIdentifier() == user.getIdentifier()) {
+ if (!channel.canBubble() || channel.isDeleted()) {
+ mBubbleData.dismissBubbleWithKey(b.getKey(), DISMISS_NO_LONGER_BUBBLE);
+ }
+ }
+ }
+ }
+
/**
* Retrieves any bubbles that are part of the notification group represented by the provided
* group key.
@@ -1279,6 +1350,7 @@ public class BubbleController {
* Updates the visibility of the bubbles based on current state.
* Does not un-bubble, just hides or un-hides.
* Updates stack description for TalkBack focus.
+ * Updates bubbles' icon views clickable states
*/
public void updateStack() {
if (mStackView == null) {
@@ -1296,6 +1368,8 @@ public class BubbleController {
}
mStackView.updateContentDescription();
+
+ mStackView.updateBubblesAcessibillityStates();
}
@VisibleForTesting
@@ -1617,6 +1691,19 @@ public class BubbleController {
}
@Override
+ public void onNotificationChannelModified(String pkg,
+ UserHandle user, NotificationChannel channel, int modificationType) {
+ // Bubbles only cares about updates or deletions.
+ if (modificationType == NOTIFICATION_CHANNEL_OR_GROUP_UPDATED
+ || modificationType == NOTIFICATION_CHANNEL_OR_GROUP_DELETED) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.onNotificationChannelModified(pkg, user, channel,
+ modificationType);
+ });
+ }
+ }
+
+ @Override
public void onStatusBarVisibilityChanged(boolean visible) {
mMainExecutor.execute(() -> {
BubbleController.this.onStatusBarVisibilityChanged(visible);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index cd635c10fd8e..51b7eaa8b0e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -562,17 +562,10 @@ public class BubbleData {
overflowBubble(reason, bubbleToRemove);
if (mBubbles.size() == 1) {
- if (hasOverflowBubbles() && (mPositioner.showingInTaskbar() || isExpanded())) {
- // No more active bubbles but we have stuff in the overflow -- select that view
- // if we're already expanded or always showing.
- setShowingOverflow(true);
- setSelectedBubbleInternal(mOverflow);
- } else {
- setExpandedInternal(false);
- // Don't use setSelectedBubbleInternal because we don't want to trigger an
- // applyUpdate
- mSelectedBubble = null;
- }
+ setExpandedInternal(false);
+ // Don't use setSelectedBubbleInternal because we don't want to trigger an
+ // applyUpdate
+ mSelectedBubble = null;
}
if (indexToRemove < mBubbles.size() - 1) {
// Removing anything but the last bubble means positions will change.
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 a87aad4261a6..9ae67a9bf227 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
@@ -335,7 +335,7 @@ public class BubbleExpandedView extends LinearLayout {
mManageButton.setVisibility(GONE);
} else {
mTaskView = new TaskView(mContext, mController.getTaskOrganizer(),
- mController.getSyncTransactionQueue());
+ mController.getTaskViewTransitions(), mController.getSyncTransactionQueue());
mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
@@ -386,13 +386,14 @@ public class BubbleExpandedView extends LinearLayout {
final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
android.R.attr.dialogCornerRadius,
android.R.attr.colorBackgroundFloating});
- mCornerRadius = ta.getDimensionPixelSize(0, 0);
+ boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+ mContext.getResources());
+ mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
mBackgroundColorFloating = ta.getColor(1, Color.WHITE);
mExpandedViewContainer.setBackgroundColor(mBackgroundColorFloating);
ta.recycle();
- if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
- mContext.getResources())) {
+ if (mTaskView != null) {
mTaskView.setCornerRadius(mCornerRadius);
}
updatePointerView();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
index b0e029fdc681..9d3bf34895d3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
@@ -21,19 +21,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.icons.BaseIconFactory;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ShadowGenerator;
import com.android.wm.shell.R;
/**
@@ -44,12 +37,9 @@ import com.android.wm.shell.R;
@VisibleForTesting
public class BubbleIconFactory extends BaseIconFactory {
- private int mBadgeSize;
-
public BubbleIconFactory(Context context) {
super(context, context.getResources().getConfiguration().densityDpi,
context.getResources().getDimensionPixelSize(R.dimen.bubble_size));
- mBadgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size);
}
/**
@@ -75,84 +65,4 @@ public class BubbleIconFactory extends BaseIconFactory {
return null;
}
}
-
- /**
- * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This
- * will include the workprofile indicator on the badge if appropriate.
- */
- BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
- ShadowGenerator shadowGenerator = new ShadowGenerator(mBadgeSize);
- Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mBadgeSize);
-
- if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
- userBadgedBitmap = Bitmap.createScaledBitmap(
- getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
- userBadgedAppIcon.getIntrinsicWidth()),
- mBadgeSize, mBadgeSize, /* filter */ true);
- }
-
- if (isImportantConversation) {
- final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.importance_ring_stroke_width);
- final int importantConversationColor = mContext.getResources().getColor(
- R.color.important_conversation, null);
- Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
- userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
- Canvas c = new Canvas(badgeAndRing);
-
- Paint ringPaint = new Paint();
- ringPaint.setStyle(Paint.Style.FILL);
- ringPaint.setColor(importantConversationColor);
- ringPaint.setAntiAlias(true);
- c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint);
-
- final int bitmapTop = (int) ringStrokeWidth;
- final int bitmapLeft = (int) ringStrokeWidth;
- final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth;
- final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth;
-
- Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth,
- bitmapHeight, /* filter */ true);
- c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null);
-
- shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
- return createIconBitmap(badgeAndRing);
- } else {
- Canvas c = new Canvas();
- c.setBitmap(userBadgedBitmap);
- shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
- return createIconBitmap(userBadgedBitmap);
- }
- }
-
- public Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
- Drawable foreground = icon.getForeground();
- Drawable background = icon.getBackground();
- Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas();
- canvas.setBitmap(bitmap);
-
- // Clip canvas to circle.
- Path circlePath = new Path();
- circlePath.addCircle(/* x */ size / 2f,
- /* y */ size / 2f,
- /* radius */ size / 2f,
- Path.Direction.CW);
- canvas.clipPath(circlePath);
-
- // Draw background.
- background.setBounds(0, 0, size, size);
- background.draw(canvas);
-
- // Draw foreground. The foreground and background drawables are derived from adaptive icons
- // Some icon shapes fill more space than others, so adaptive icons are normalized to about
- // the same size. This size is smaller than the original bounds, so we estimate
- // the difference in this offset.
- int offset = size / 5;
- foreground.setBounds(-offset, -offset, size + offset, size + offset);
- foreground.draw(canvas);
-
- canvas.setBitmap(null);
- return bitmap;
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 0c3a6b2dbd84..dd751d24e770 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -66,7 +66,7 @@ class BubbleOverflow(
updateResources()
getExpandedView()?.applyThemeAttrs()
// Apply inset and new style to fresh icon drawable.
- getIconView()?.setImageResource(R.drawable.bubble_ic_overflow_button)
+ getIconView()?.setIconImageResource(R.drawable.bubble_ic_overflow_button)
updateBtnTheme()
}
@@ -89,20 +89,19 @@ class BubbleOverflow(
dotColor = colorAccent
val shapeColor = res.getColor(android.R.color.system_accent1_1000)
- overflowBtn?.drawable?.setTint(shapeColor)
+ overflowBtn?.iconDrawable?.setTint(shapeColor)
val iconFactory = BubbleIconFactory(context)
// Update bitmap
- val fg = InsetDrawable(overflowBtn?.drawable, overflowIconInset)
- bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(
- ColorDrawable(colorAccent), fg),
- null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+ val fg = InsetDrawable(overflowBtn?.iconDrawable, overflowIconInset)
+ bitmap = iconFactory.createBadgedIconBitmap(
+ AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)).icon
// Update dot path
dotPath = PathParser.createPathFromPathData(
res.getString(com.android.internal.R.string.config_icon_mask))
- val scale = iconFactory.normalizer.getScale(iconView!!.drawable,
+ val scale = iconFactory.normalizer.getScale(iconView!!.iconDrawable,
null /* outBounds */, null /* path */, null /* outMaskShape */)
val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
val matrix = Matrix()
@@ -142,6 +141,10 @@ class BubbleOverflow(
return null
}
+ override fun getRawAppBadge(): Bitmap? {
+ return null
+ }
+
override fun getBubbleIcon(): Bitmap {
return bitmap
}
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 620c291b357b..7ab683513570 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
@@ -70,6 +70,7 @@ import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
@@ -822,7 +823,9 @@ public class BubbleStackView extends FrameLayout
mAnimatingOutSurfaceView = new SurfaceView(getContext());
mAnimatingOutSurfaceView.setUseAlpha();
mAnimatingOutSurfaceView.setZOrderOnTop(true);
- mAnimatingOutSurfaceView.setCornerRadius(mCornerRadius);
+ boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+ mContext.getResources());
+ mAnimatingOutSurfaceView.setCornerRadius(supportsRoundedCorners ? mCornerRadius : 0);
mAnimatingOutSurfaceView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
mAnimatingOutSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
@@ -1482,6 +1485,69 @@ public class BubbleStackView extends FrameLayout
}
}
+ /**
+ * Update bubbles' icon views accessibility states.
+ */
+ public void updateBubblesAcessibillityStates() {
+ for (int i = 0; i < mBubbleData.getBubbles().size(); i++) {
+ Bubble prevBubble = i > 0 ? mBubbleData.getBubbles().get(i - 1) : null;
+ Bubble bubble = mBubbleData.getBubbles().get(i);
+
+ View bubbleIconView = bubble.getIconView();
+ if (bubbleIconView == null) {
+ continue;
+ }
+
+ if (mIsExpanded) {
+ // when stack is expanded
+ // all bubbles are important for accessibility
+ bubbleIconView
+ .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+
+ View prevBubbleIconView = prevBubble != null ? prevBubble.getIconView() : null;
+
+ if (prevBubbleIconView != null) {
+ bubbleIconView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View v,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(v, info);
+ info.setTraversalAfter(prevBubbleIconView);
+ }
+ });
+ }
+ } else {
+ // when stack is collapsed, only the top bubble is important for accessibility,
+ bubbleIconView.setImportantForAccessibility(
+ i == 0 ? View.IMPORTANT_FOR_ACCESSIBILITY_YES :
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ }
+ }
+
+ if (mIsExpanded) {
+ // make the overflow bubble last in the accessibility traversal order
+
+ View bubbleOverflowIconView =
+ mBubbleOverflow != null ? mBubbleOverflow.getIconView() : null;
+ if (bubbleOverflowIconView != null && !mBubbleData.getBubbles().isEmpty()) {
+ Bubble lastBubble =
+ mBubbleData.getBubbles().get(mBubbleData.getBubbles().size() - 1);
+ View lastBubbleIconView = lastBubble.getIconView();
+ if (lastBubbleIconView != null) {
+ bubbleOverflowIconView.setAccessibilityDelegate(
+ new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View v,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(v, info);
+ info.setTraversalAfter(lastBubbleIconView);
+ }
+ });
+ }
+ }
+ }
+ }
+
private void updateSystemGestureExcludeRects() {
// Exclude the region occupied by the first BubbleView in the stack
Rect excludeZone = mSystemGestureExclusionRects.get(0);
@@ -1724,6 +1790,7 @@ public class BubbleStackView extends FrameLayout
/**
* Changes the expanded state of the stack.
+ * Don't call this directly, call mBubbleData#setExpanded.
*
* @param shouldExpand whether the bubble stack should appear expanded
*/
@@ -1770,7 +1837,7 @@ public class BubbleStackView extends FrameLayout
} else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
mManageEduView.hide();
} else {
- setExpanded(false);
+ mBubbleData.setExpanded(false);
}
}
}
@@ -2613,11 +2680,13 @@ public class BubbleStackView extends FrameLayout
// If available, update the manage menu's settings option with the expanded bubble's app
// name and icon.
- if (show && mBubbleData.hasBubbleInStackWithKey(mExpandedBubble.getKey())) {
+ if (show) {
final Bubble bubble = mBubbleData.getBubbleInStackWithKey(mExpandedBubble.getKey());
- mManageSettingsIcon.setImageBitmap(bubble.getAppBadge());
- mManageSettingsText.setText(getResources().getString(
- R.string.bubbles_app_settings, bubble.getAppName()));
+ if (bubble != null) {
+ mManageSettingsIcon.setImageBitmap(bubble.getRawAppBadge());
+ mManageSettingsText.setText(getResources().getString(
+ R.string.bubbles_app_settings, bubble.getAppName()));
+ }
}
if (mExpandedBubble.getExpandedView().getTaskView() != null) {
@@ -3011,6 +3080,16 @@ public class BubbleStackView extends FrameLayout
}
/**
+ * Handles vertical offset changes, e.g. when one handed mode is switched on/off.
+ *
+ * @param offset new vertical offset.
+ */
+ void onVerticalOffsetChanged(int offset) {
+ // adjust dismiss view vertical position, so that it is still visible to the user
+ mDismissView.setPadding(/* left = */ 0, /* top = */ 0, /* right = */ 0, offset);
+ }
+
+ /**
* Holds some commonly queried information about the stack.
*/
public static class StackViewState {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 932f879caef8..69762c9bc06a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -71,6 +71,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
private WeakReference<BubbleController> mController;
private WeakReference<BubbleStackView> mStackView;
private BubbleIconFactory mIconFactory;
+ private BubbleBadgeIconFactory mBadgeIconFactory;
private boolean mSkipInflation;
private Callback mCallback;
private Executor mMainExecutor;
@@ -84,6 +85,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
BubbleController controller,
BubbleStackView stackView,
BubbleIconFactory factory,
+ BubbleBadgeIconFactory badgeFactory,
boolean skipInflation,
Callback c,
Executor mainExecutor) {
@@ -92,6 +94,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
mController = new WeakReference<>(controller);
mStackView = new WeakReference<>(stackView);
mIconFactory = factory;
+ mBadgeIconFactory = badgeFactory;
mSkipInflation = skipInflation;
mCallback = c;
mMainExecutor = mainExecutor;
@@ -100,7 +103,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
@Override
protected BubbleViewInfo doInBackground(Void... voids) {
return BubbleViewInfo.populate(mContext.get(), mController.get(), mStackView.get(),
- mIconFactory, mBubble, mSkipInflation);
+ mIconFactory, mBadgeIconFactory, mBubble, mSkipInflation);
}
@Override
@@ -127,6 +130,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
String appName;
Bitmap bubbleBitmap;
Bitmap badgeBitmap;
+ Bitmap mRawBadgeBitmap;
int dotColor;
Path dotPath;
Bubble.FlyoutMessage flyoutMessage;
@@ -134,7 +138,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
@VisibleForTesting
@Nullable
public static BubbleViewInfo populate(Context c, BubbleController controller,
- BubbleStackView stackView, BubbleIconFactory iconFactory, Bubble b,
+ BubbleStackView stackView, BubbleIconFactory iconFactory,
+ BubbleBadgeIconFactory badgeIconFactory, Bubble b,
boolean skipInflation) {
BubbleViewInfo info = new BubbleViewInfo();
@@ -186,12 +191,12 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
bubbleDrawable = appIcon;
}
- BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
+ BitmapInfo badgeBitmapInfo = badgeIconFactory.getBadgeBitmap(badgedIcon,
b.isImportantConversation());
info.badgeBitmap = badgeBitmapInfo.icon;
- info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable,
- null /* user */,
- true /* shrinkNonAdaptiveIcons */).icon;
+ // Raw badge bitmap never includes the important conversation ring
+ info.mRawBadgeBitmap = badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon;
+ info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon;
// Dot color & placement
Path iconPath = PathParser.createPathFromPathData(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
index 7e552826e94a..3f6d41bb2b68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
@@ -43,6 +43,9 @@ public interface BubbleViewProvider {
/** App badge drawable to draw above bubble icon. */
@Nullable Bitmap getAppBadge();
+ /** Base app badge drawable without any markings. */
+ @Nullable Bitmap getRawAppBadge();
+
/** Path of normalized bubble icon to draw dot on. */
Path getDotPath();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index c82249b8a369..af403d23d69c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -21,9 +21,12 @@ import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.app.NotificationChannel;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
import android.util.Pair;
@@ -191,10 +194,26 @@ public interface Bubbles {
* @param entryDataByKey a map of ranking key to bubble entry and whether the entry should
* bubble up
*/
- void onRankingUpdated(RankingMap rankingMap,
+ void onRankingUpdated(
+ RankingMap rankingMap,
HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey);
/**
+ * Called when a notification channel is modified, in response to
+ * {@link NotificationListenerService#onNotificationChannelModified}.
+ *
+ * @param pkg the package the notification channel belongs to.
+ * @param user the user the notification channel belongs to.
+ * @param channel the channel being modified.
+ * @param modificationType the type of modification that occurred to the channel.
+ */
+ void onNotificationChannelModified(
+ String pkg,
+ UserHandle user,
+ NotificationChannel channel,
+ int modificationType);
+
+ /**
* Called when the status bar has become visible or invisible (either permanently or
* temporarily).
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index ffda1f92ec90..c32733d4f73c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -27,7 +27,6 @@ import androidx.annotation.BinderThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -71,12 +70,18 @@ public class DisplayChangeController {
mRotationListener.remove(listener);
}
+ /** Query all listeners for changes that should happen on rotation. */
+ public void dispatchOnRotateDisplay(WindowContainerTransaction outWct, int displayId,
+ final int fromRotation, final int toRotation) {
+ for (OnDisplayChangingListener c : mRotationListener) {
+ c.onRotateDisplay(displayId, fromRotation, toRotation, outWct);
+ }
+ }
+
private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation,
IDisplayWindowRotationCallback callback) {
WindowContainerTransaction t = new WindowContainerTransaction();
- for (OnDisplayChangingListener c : mRotationListener) {
- c.onRotateDisplay(displayId, fromRotation, toRotation, t);
- }
+ dispatchOnRotateDisplay(t, displayId, fromRotation, toRotation);
try {
callback.continueRotateDisplay(toRotation, t);
} catch (RemoteException e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index a1fb658ccb9d..9384e2b4dfdf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.common;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.util.Slog;
@@ -34,6 +35,7 @@ import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingList
import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.ArrayList;
+import java.util.List;
/**
* This module deals with display rotations coming from WM. When WM starts a rotation: after it has
@@ -76,6 +78,11 @@ public class DisplayController {
}
}
+ /** Get the DisplayChangeController. */
+ public DisplayChangeController getChangeController() {
+ return mChangeController;
+ }
+
/**
* Gets a display by id from DisplayManager.
*/
@@ -238,6 +245,21 @@ public class DisplayController {
}
}
+ private void onKeepClearAreasChanged(int displayId, List<Rect> restricted,
+ List<Rect> unrestricted) {
+ synchronized (mDisplays) {
+ if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
+ Slog.w(TAG, "Skipping onKeepClearAreasChanged on unknown"
+ + " display, displayId=" + displayId);
+ return;
+ }
+ for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+ mDisplayChangedListeners.get(i)
+ .onKeepClearAreasChanged(displayId, restricted, unrestricted);
+ }
+ }
+ }
+
private static class DisplayRecord {
private int mDisplayId;
private Context mContext;
@@ -296,6 +318,14 @@ public class DisplayController {
DisplayController.this.onFixedRotationFinished(displayId);
});
}
+
+ @Override
+ public void onKeepClearAreasChanged(int displayId, List<Rect> restricted,
+ List<Rect> unrestricted) {
+ mMainExecutor.execute(() -> {
+ DisplayController.this.onKeepClearAreasChanged(displayId, restricted, unrestricted);
+ });
+ }
}
/**
@@ -330,5 +360,11 @@ public class DisplayController {
* Called when fixed rotation on a display is finished.
*/
default void onFixedRotationFinished(int displayId) {}
+
+ /**
+ * Called when keep-clear areas on a display have changed.
+ */
+ default void onKeepClearAreasChanged(int displayId, List<Rect> restricted,
+ List<Rect> unrestricted) {}
}
}
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 eea6e3cb35db..c4bd73ba1b4a 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.common;
-import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.SurfaceControl;
@@ -63,8 +62,6 @@ public class ScreenshotUtils {
if (buffer == null || buffer.getHardwareBuffer() == null) {
return;
}
- final GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- buffer.getHardwareBuffer());
mScreenshot = new SurfaceControl.Builder()
.setName("ScreenshotUtils screenshot")
.setFormat(PixelFormat.TRANSLUCENT)
@@ -73,7 +70,7 @@ public class ScreenshotUtils {
.setBLASTLayer()
.build();
- mTransaction.setBuffer(mScreenshot, graphicBuffer);
+ mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer());
mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
mTransaction.reparent(mScreenshot, mSurfaceControl);
mTransaction.setLayer(mScreenshot, mLayer);
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 97c89d042be0..1039e2ac4fd9 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
@@ -21,7 +21,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
@@ -335,9 +334,6 @@ public class SystemWindows {
boolean alwaysConsumeSystemBars, int displayId) {}
@Override
- public void locationInParentDisplayChanged(Point offset) {}
-
- @Override
public void insetsChanged(InsetsState insetsState, boolean willMove, boolean willResize) {}
@Override
@@ -360,9 +356,6 @@ public class SystemWindows {
public void dispatchGetNewSurface() {}
@Override
- public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {}
-
- @Override
public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
index 9e012598554b..aac1d0626d30 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
@@ -17,13 +17,10 @@ package com.android.wm.shell.common.magnetictarget
import android.annotation.SuppressLint
import android.content.Context
-import android.database.ContentObserver
import android.graphics.PointF
-import android.os.Handler
-import android.os.UserHandle
+import android.os.VibrationAttributes
import android.os.VibrationEffect
import android.os.Vibrator
-import android.provider.Settings
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
@@ -147,6 +144,8 @@ abstract class MagnetizedObject<T : Any>(
private val velocityTracker: VelocityTracker = VelocityTracker.obtain()
private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+ private val vibrationAttributes: VibrationAttributes = VibrationAttributes.createForUsage(
+ VibrationAttributes.USAGE_TOUCH)
private var touchDown = PointF()
private var touchSlop = 0
@@ -268,10 +267,6 @@ abstract class MagnetizedObject<T : Any>(
*/
var flungIntoTargetSpringConfig = springConfig
- init {
- initHapticSettingObserver(context)
- }
-
/**
* Adds the provided MagneticTarget to this object. The object will now be attracted to the
* target if it strays within its magnetic field or is flung towards it.
@@ -468,8 +463,8 @@ abstract class MagnetizedObject<T : Any>(
/** Plays the given vibration effect if haptics are enabled. */
@SuppressLint("MissingPermission")
private fun vibrateIfEnabled(effectId: Int) {
- if (hapticsEnabled && systemHapticsEnabled) {
- vibrator.vibrate(VibrationEffect.createPredefined(effectId))
+ if (hapticsEnabled) {
+ vibrator.vibrate(VibrationEffect.createPredefined(effectId), vibrationAttributes)
}
}
@@ -622,44 +617,6 @@ abstract class MagnetizedObject<T : Any>(
}
companion object {
-
- /**
- * Whether the HAPTIC_FEEDBACK_ENABLED setting is true.
- *
- * We put it in the companion object because we need to register a settings observer and
- * [MagnetizedObject] doesn't have an obvious lifecycle so we don't have a good time to
- * remove that observer. Since this settings is shared among all instances we just let all
- * instances read from this value.
- */
- private var systemHapticsEnabled = false
- private var hapticSettingObserverInitialized = false
-
- private fun initHapticSettingObserver(context: Context) {
- if (hapticSettingObserverInitialized) {
- return
- }
-
- val hapticSettingObserver =
- object : ContentObserver(Handler.getMain()) {
- override fun onChange(selfChange: Boolean) {
- systemHapticsEnabled =
- Settings.System.getIntForUser(
- context.contentResolver,
- Settings.System.HAPTIC_FEEDBACK_ENABLED,
- 0,
- UserHandle.USER_CURRENT) != 0
- }
- }
-
- context.contentResolver.registerContentObserver(
- Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED),
- true /* notifyForDescendants */, hapticSettingObserver)
-
- // Trigger the observer once to initialize systemHapticsEnabled.
- hapticSettingObserver.onChange(false /* selfChange */)
- hapticSettingObserverInitialized = true
- }
-
/**
* Magnetizes the given view. Magnetized views are attracted to one or more magnetic
* targets. Magnetic targets attract objects that are dragged near them, and hold them there
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index ba343cb12085..f7c92fed5522 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -93,7 +93,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private final InsetsState mInsetsState = new InsetsState();
private Context mContext;
- private DividerSnapAlgorithm mDividerSnapAlgorithm;
+ @VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
private WindowContainerToken mWinToken1;
private WindowContainerToken mWinToken2;
private int mDividePosition;
@@ -123,7 +123,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mDividerWindowWidth = mDividerSize + 2 * mDividerInsets;
mRootBounds.set(configuration.windowConfiguration.getBounds());
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null);
resetDividerPosition();
}
@@ -180,8 +180,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
/** Applies new configuration, returns {@code false} if there's no effect to the layout. */
public boolean updateConfiguration(Configuration configuration) {
- boolean affectsLayout = false;
-
// Update the split bounds when necessary. Besides root bounds changed, split bounds need to
// be updated when the rotation changed to cover the case that users rotated the screen 180
// degrees.
@@ -203,7 +201,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mTempRect.set(mRootBounds);
mRootBounds.set(rootBounds);
mRotation = rotation;
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null);
initDividerPosition(mTempRect);
if (mInitialized) {
@@ -214,6 +212,25 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
return true;
}
+ /** Rotate the layout to specific rotation and calculate new bounds. The stable insets value
+ * should be calculated by display layout. */
+ public void rotateTo(int newRotation, Rect stableInsets) {
+ final int rotationDelta = (newRotation - mRotation + 4) % 4;
+ final boolean changeOrient = (rotationDelta % 2) != 0;
+
+ mRotation = newRotation;
+ Rect tmpRect = new Rect(mRootBounds);
+ if (changeOrient) {
+ tmpRect.set(mRootBounds.top, mRootBounds.left, mRootBounds.bottom, mRootBounds.right);
+ }
+
+ // We only need new bounds here, other configuration should be update later.
+ mTempRect.set(mRootBounds);
+ mRootBounds.set(tmpRect);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, stableInsets);
+ initDividerPosition(mTempRect);
+ }
+
private void initDividerPosition(Rect oldBounds) {
final float snapRatio = (float) mDividePosition
/ (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height());
@@ -294,20 +311,22 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mSplitLayoutHandler.onLayoutSizeChanging(this);
}
- void setDividePosition(int position) {
+ void setDividePosition(int position, boolean applyLayoutChange) {
mDividePosition = position;
updateBounds(mDividePosition);
- mSplitLayoutHandler.onLayoutSizeChanged(this);
+ if (applyLayoutChange) {
+ mSplitLayoutHandler.onLayoutSizeChanged(this);
+ }
}
- /** Sets divide position base on the ratio within root bounds. */
+ /** Updates divide position and split bounds base on the ratio within root bounds. */
public void setDivideRatio(float ratio) {
final int position = isLandscape()
? mRootBounds.left + (int) (mRootBounds.width() * ratio)
: mRootBounds.top + (int) (mRootBounds.height() * ratio);
- DividerSnapAlgorithm.SnapTarget snapTarget =
+ final DividerSnapAlgorithm.SnapTarget snapTarget =
mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
- setDividePosition(snapTarget.position);
+ setDividePosition(snapTarget.position, false /* applyLayoutChange */);
}
/** Resets divider position. */
@@ -335,7 +354,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
() -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */));
break;
default:
- flingDividePosition(currentPosition, snapTarget.position, null);
+ flingDividePosition(currentPosition, snapTarget.position,
+ () -> setDividePosition(snapTarget.position, true /* applyLayoutChange */));
break;
}
}
@@ -353,7 +373,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss);
}
- private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds) {
+ private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds,
+ @Nullable Rect stableInsets) {
final boolean isLandscape = isLandscape(rootBounds);
return new DividerSnapAlgorithm(
context.getResources(),
@@ -361,7 +382,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
rootBounds.height(),
mDividerSize,
!isLandscape,
- getDisplayInsets(context),
+ stableInsets != null ? stableInsets : getDisplayInsets(context),
isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
}
@@ -381,7 +402,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- setDividePosition(to);
if (flingFinishedCallback != null) {
flingFinishedCallback.run();
}
@@ -389,7 +409,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@Override
public void onAnimationCancel(Animator animation) {
- setDividePosition(to);
+ setDividePosition(to, true /* applyLayoutChange */);
}
});
animator.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index e0b23873a980..4d279bc4e927 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -17,6 +17,8 @@
package com.android.wm.shell.compatui;
import android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
@@ -47,18 +49,20 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
/**
- * Controls to show/update restart-activity buttons on Tasks based on whether the foreground
+ * Controller to show/update compat UI components on Tasks based on whether the foreground
* activities are in compatibility mode.
*/
public class CompatUIController implements OnDisplaysChangedListener,
DisplayImeController.ImePositionProcessor {
- /** Callback for size compat UI interaction. */
+ /** Callback for compat UI interaction. */
public interface CompatUICallback {
/** Called when the size compat restart button appears. */
void onSizeCompatRestartButtonAppeared(int taskId);
/** Called when the size compat restart button is clicked. */
void onSizeCompatRestartButtonClicked(int taskId);
+ /** Called when the camera compat control state is updated. */
+ void onCameraControlStateUpdated(int taskId, @CameraCompatControlState int state);
}
private static final String TAG = "CompatUIController";
@@ -86,10 +90,12 @@ public class CompatUIController implements OnDisplaysChangedListener,
private CompatUICallback mCallback;
- /** Only show once automatically in the process life. */
- private boolean mHasShownHint;
- /** Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
- * be shown. */
+ // Only show once automatically in the process life.
+ private boolean mHasShownSizeCompatHint;
+ private boolean mHasShownCameraCompatHint;
+
+ // Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
+ // be shown.
private boolean mKeyguardOccluded;
public CompatUIController(Context context,
@@ -122,23 +128,20 @@ public class CompatUIController implements OnDisplaysChangedListener,
* Called when the Task info changed. Creates and updates the compat UI if there is an
* activity in size compat, or removes the UI if there is no size compat activity.
*
- * @param displayId display the task and activity are in.
- * @param taskId task the activity is in.
- * @param taskConfig task config to place the compat UI with.
+ * @param taskInfo {@link TaskInfo} task the activity is in.
* @param taskListener listener to handle the Task Surface placement.
*/
- public void onCompatInfoChanged(int displayId, int taskId,
- @Nullable Configuration taskConfig,
+ public void onCompatInfoChanged(TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
- if (taskConfig == null || taskListener == null) {
+ if (taskInfo.configuration == null || taskListener == null) {
// Null token means the current foreground activity is not in compatibility mode.
- removeLayout(taskId);
- } else if (mActiveLayouts.contains(taskId)) {
+ removeLayout(taskInfo.taskId);
+ } else if (mActiveLayouts.contains(taskInfo.taskId)) {
// UI already exists, update the UI layout.
- updateLayout(taskId, taskConfig, taskListener);
+ updateLayout(taskInfo, taskListener);
} else {
// Create a new compat UI.
- createLayout(displayId, taskId, taskConfig, taskListener);
+ createLayout(taskInfo, taskListener);
}
}
@@ -215,38 +218,42 @@ public class CompatUIController implements OnDisplaysChangedListener,
return mDisplaysWithIme.contains(displayId);
}
- private void createLayout(int displayId, int taskId, Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener) {
- final Context context = getOrCreateDisplayContext(displayId);
+ private void createLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+ final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
- Log.e(TAG, "Cannot get context for display " + displayId);
+ Log.e(TAG, "Cannot get context for display " + taskInfo.displayId);
return;
}
final CompatUIWindowManager compatUIWindowManager =
- createLayout(context, displayId, taskId, taskConfig, taskListener);
- mActiveLayouts.put(taskId, compatUIWindowManager);
- compatUIWindowManager.createLayout(showOnDisplay(displayId));
+ createLayout(context, taskInfo, taskListener);
+ mActiveLayouts.put(taskInfo.taskId, compatUIWindowManager);
+ compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId), taskInfo);
}
@VisibleForTesting
- CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
- Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
+ CompatUIWindowManager createLayout(Context context, TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
- taskConfig, mSyncQueue, mCallback, taskId, taskListener,
- mDisplayController.getDisplayLayout(displayId), mHasShownHint);
- // Only show hint for the first time.
- mHasShownHint = true;
+ taskInfo.configuration, mSyncQueue, mCallback, taskInfo.taskId, taskListener,
+ mDisplayController.getDisplayLayout(taskInfo.displayId), mHasShownSizeCompatHint,
+ mHasShownCameraCompatHint);
+ // Only show hints for the first time.
+ if (taskInfo.topActivityInSizeCompat) {
+ mHasShownSizeCompatHint = true;
+ }
+ if (taskInfo.hasCameraCompatControl()) {
+ mHasShownCameraCompatHint = true;
+ }
return compatUIWindowManager;
}
- private void updateLayout(int taskId, Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener) {
- final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
+ private void updateLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+ final CompatUIWindowManager layout = mActiveLayouts.get(taskInfo.taskId);
if (layout == null) {
return;
}
- layout.updateCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
+ layout.updateCompatInfo(taskInfo, taskListener, showOnDisplay(layout.getDisplayId()));
}
private void removeLayout(int taskId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
index ea4f20968438..d44b4d8f63b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.compatui;
+import android.annotation.IdRes;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
@@ -28,7 +31,7 @@ import com.android.wm.shell.R;
/**
* Container for compat UI controls.
*/
-public class CompatUILayout extends LinearLayout {
+class CompatUILayout extends LinearLayout {
private CompatUIWindowManager mWindowManager;
@@ -53,21 +56,59 @@ public class CompatUILayout extends LinearLayout {
mWindowManager = windowManager;
}
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- // Need to relayout after changes like hiding / showing a hint since they affect size.
- // Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
- mWindowManager.relayout();
+ void updateCameraTreatmentButton(@CameraCompatControlState int newState) {
+ int buttonBkgId = newState == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? R.drawable.camera_compat_treatment_suggested_ripple
+ : R.drawable.camera_compat_treatment_applied_ripple;
+ int hintStringId = newState == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? R.string.camera_compat_treatment_suggested_button_description
+ : R.string.camera_compat_treatment_applied_button_description;
+ final ImageButton button = findViewById(R.id.camera_compat_treatment_button);
+ button.setImageResource(buttonBkgId);
+ button.setContentDescription(getResources().getString(hintStringId));
+ final LinearLayout hint = findViewById(R.id.camera_compat_hint);
+ ((TextView) hint.findViewById(R.id.compat_mode_hint_text)).setText(hintStringId);
}
void setSizeCompatHintVisibility(boolean show) {
- final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
+ setViewVisibility(R.id.size_compat_hint, show);
+ }
+
+ void setCameraCompatHintVisibility(boolean show) {
+ setViewVisibility(R.id.camera_compat_hint, show);
+ }
+
+ void setRestartButtonVisibility(boolean show) {
+ setViewVisibility(R.id.size_compat_restart_button, show);
+ // Hint should never be visible without button.
+ if (!show) {
+ setSizeCompatHintVisibility(/* show= */ false);
+ }
+ }
+
+ void setCameraControlVisibility(boolean show) {
+ setViewVisibility(R.id.camera_compat_control, show);
+ // Hint should never be visible without button.
+ if (!show) {
+ setCameraCompatHintVisibility(/* show= */ false);
+ }
+ }
+
+ private void setViewVisibility(@IdRes int resId, boolean show) {
+ final View view = findViewById(resId);
int visibility = show ? View.VISIBLE : View.GONE;
- if (sizeCompatHint.getVisibility() == visibility) {
+ if (view.getVisibility() == visibility) {
return;
}
- sizeCompatHint.setVisibility(visibility);
+ view.setVisibility(visibility);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ // Need to relayout after changes like hiding / showing a hint since they affect size.
+ // Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
+ mWindowManager.relayout();
}
@Override
@@ -85,5 +126,26 @@ public class CompatUILayout extends LinearLayout {
((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text))
.setText(R.string.restart_button_description);
sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false));
+
+ final ImageButton cameraTreatmentButton =
+ findViewById(R.id.camera_compat_treatment_button);
+ cameraTreatmentButton.setOnClickListener(
+ view -> mWindowManager.onCameraTreatmentButtonClicked());
+ cameraTreatmentButton.setOnLongClickListener(view -> {
+ mWindowManager.onCameraButtonLongClicked();
+ return true;
+ });
+
+ final ImageButton cameraDismissButton = findViewById(R.id.camera_compat_dismiss_button);
+ cameraDismissButton.setOnClickListener(
+ view -> mWindowManager.onCameraDismissButtonClicked());
+ cameraDismissButton.setOnLongClickListener(view -> {
+ mWindowManager.onCameraButtonLongClicked();
+ return true;
+ });
+
+ final LinearLayout cameraCompatHint = findViewById(R.id.camera_compat_hint);
+ cameraCompatHint.setOnClickListener(
+ view -> setCameraCompatHintVisibility(/* show= */ false));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 997ad04e3b57..9c001a37e4b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -16,27 +16,20 @@
package com.android.wm.shell.compatui;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-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_APPLICATION_OVERLAY;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+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 android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.os.Binder;
import android.util.Log;
-import android.view.IWindow;
import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowlessWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
@@ -45,149 +38,108 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
/**
- * Holds view hierarchy of a root surface and helps to inflate and manage layout for compat
- * controls.
+ * Window manager for the Size Compat restart button and Camera Compat control.
*/
-class CompatUIWindowManager extends WindowlessWindowManager {
+class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
- private static final String TAG = "CompatUIWindowManager";
+ /**
+ * The Compat UI should be the topmost child of the Task in case there can be more than one
+ * child.
+ */
+ private static final int Z_ORDER = Integer.MAX_VALUE;
- private final SyncTransactionQueue mSyncQueue;
private final CompatUIController.CompatUICallback mCallback;
- private final int mDisplayId;
- private final int mTaskId;
- private final Rect mStableBounds;
-
- private Context mContext;
- private Configuration mTaskConfig;
- private ShellTaskOrganizer.TaskListener mTaskListener;
- private DisplayLayout mDisplayLayout;
+ // Remember the last reported states in case visibility changes due to keyguard or IME updates.
@VisibleForTesting
- boolean mShouldShowHint;
+ boolean mHasSizeCompat;
+ @CameraCompatControlState
+ private int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
- @Nullable
@VisibleForTesting
- CompatUILayout mCompatUILayout;
+ boolean mShouldShowSizeCompatHint;
+ @VisibleForTesting
+ boolean mShouldShowCameraCompatHint;
@Nullable
- private SurfaceControlViewHost mViewHost;
- @Nullable
- private SurfaceControl mLeash;
+ @VisibleForTesting
+ CompatUILayout mLayout;
CompatUIWindowManager(Context context, Configuration taskConfig,
SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
- boolean hasShownHint) {
- super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
- mContext = context;
- mSyncQueue = syncQueue;
+ boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
+ super(context, taskConfig, syncQueue, taskId, taskListener, displayLayout);
mCallback = callback;
- mTaskConfig = taskConfig;
- mDisplayId = mContext.getDisplayId();
- mTaskId = taskId;
- mTaskListener = taskListener;
- mDisplayLayout = displayLayout;
- mShouldShowHint = !hasShownHint;
- mStableBounds = new Rect();
- mDisplayLayout.getStableBounds(mStableBounds);
+ mShouldShowSizeCompatHint = !hasShownSizeCompatHint;
+ mShouldShowCameraCompatHint = !hasShownCameraCompatHint;
}
@Override
- public void setConfiguration(Configuration configuration) {
- super.setConfiguration(configuration);
- mContext = mContext.createConfigurationContext(configuration);
+ protected int getZOrder() {
+ return Z_ORDER;
}
+
@Override
- protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
- // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
- .setContainerLayer()
- .setName("CompatUILeash")
- .setHidden(false)
- .setCallsite("CompatUIWindowManager#attachToParentSurface");
- attachToParentSurface(builder);
- mLeash = builder.build();
- b.setParent(mLeash);
+ protected @Nullable View getLayout() {
+ return mLayout;
}
- /** Creates the layout for compat controls. */
- void createLayout(boolean show) {
- if (!show || mCompatUILayout != null) {
- // Wait until compat controls should be visible.
- return;
- }
-
- initCompatUi();
- updateSurfacePosition();
+ @Override
+ protected void removeLayout() {
+ mLayout = null;
+ }
- mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
+ @Override
+ protected boolean eligibleToShowLayout() {
+ return mHasSizeCompat || shouldShowCameraControl();
}
- /** Called when compat info changed. */
- void updateCompatInfo(Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener, boolean show) {
- final Configuration prevTaskConfig = mTaskConfig;
- final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
- mTaskConfig = taskConfig;
- mTaskListener = taskListener;
-
- // Update configuration.
- mContext = mContext.createConfigurationContext(taskConfig);
- setConfiguration(taskConfig);
-
- if (mCompatUILayout == null || prevTaskListener != taskListener) {
- // TaskListener changed, recreate the layout for new surface parent.
- release();
- createLayout(show);
- return;
- }
+ /**
+ * Updates the internal state with respect to {@code taskInfo} and calls {@link
+ * #createLayout(boolean)}.
+ */
+ void createLayout(boolean canShow, TaskInfo taskInfo) {
+ mHasSizeCompat = taskInfo.topActivityInSizeCompat;
+ mCameraCompatControlState = taskInfo.cameraCompatControlState;
+ createLayout(canShow);
+ }
- if (!taskConfig.windowConfiguration.getBounds()
- .equals(prevTaskConfig.windowConfiguration.getBounds())) {
- // Reposition the UI surfaces.
- updateSurfacePosition();
- }
+ @Override
+ protected View createLayout() {
+ mLayout = inflateLayout();
+ mLayout.inject(this);
- if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
- // Update layout for RTL.
- mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection());
- updateSurfacePosition();
- }
- }
+ updateVisibilityOfViews();
- /** Called when the visibility of the UI should change. */
- void updateVisibility(boolean show) {
- if (mCompatUILayout == null) {
- // Layout may not have been created because it was hidden previously.
- createLayout(show);
- return;
+ if (mHasSizeCompat) {
+ mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
}
- // Hide compat UIs when IME is showing.
- final int newVisibility = show ? View.VISIBLE : View.GONE;
- if (mCompatUILayout.getVisibility() != newVisibility) {
- mCompatUILayout.setVisibility(newVisibility);
- }
+ return mLayout;
}
- /** Called when display layout changed. */
- void updateDisplayLayout(DisplayLayout displayLayout) {
- final Rect prevStableBounds = mStableBounds;
- final Rect curStableBounds = new Rect();
- displayLayout.getStableBounds(curStableBounds);
- mDisplayLayout = displayLayout;
- if (!prevStableBounds.equals(curStableBounds)) {
- // Stable bounds changed, update UI surface positions.
- updateSurfacePosition();
- mStableBounds.set(curStableBounds);
- }
+ @VisibleForTesting
+ CompatUILayout inflateLayout() {
+ return (CompatUILayout) LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout,
+ null);
}
- /** Called when it is ready to be placed compat UI surface. */
- void attachToParentSurface(SurfaceControl.Builder b) {
- mTaskListener.attachChildSurfaceToTask(mTaskId, b);
+ @Override
+ public void updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
+ boolean canShow) {
+ final boolean prevHasSizeCompat = mHasSizeCompat;
+ final int prevCameraCompatControlState = mCameraCompatControlState;
+ mHasSizeCompat = taskInfo.topActivityInSizeCompat;
+ mCameraCompatControlState = taskInfo.cameraCompatControlState;
+
+ super.updateCompatInfo(taskInfo, taskListener, canShow);
+
+ if (prevHasSizeCompat != mHasSizeCompat
+ || prevCameraCompatControlState != mCameraCompatControlState) {
+ updateVisibilityOfViews();
+ }
}
/** Called when the restart button is clicked. */
@@ -195,127 +147,91 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mCallback.onSizeCompatRestartButtonClicked(mTaskId);
}
- /** Called when the restart button is long clicked. */
- void onRestartButtonLongClicked() {
- if (mCompatUILayout == null) {
+ /** Called when the camera treatment button is clicked. */
+ void onCameraTreatmentButtonClicked() {
+ if (!shouldShowCameraControl()) {
+ Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state.");
return;
}
- mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
- }
-
- int getDisplayId() {
- return mDisplayId;
- }
-
- int getTaskId() {
- return mTaskId;
- }
-
- /** Releases the surface control and tears down the view hierarchy. */
- void release() {
- mCompatUILayout = null;
-
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
+ // When a camera control is shown, only two states are allowed: "treament applied" and
+ // "treatment suggested". Clicks on the conrol's treatment button toggle between these
+ // two states.
+ mCameraCompatControlState =
+ mCameraCompatControlState == CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
+ : CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ mCallback.onCameraControlStateUpdated(mTaskId, mCameraCompatControlState);
+ mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ }
+
+ /** Called when the camera dismiss button is clicked. */
+ void onCameraDismissButtonClicked() {
+ if (!shouldShowCameraControl()) {
+ Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state.");
+ return;
}
+ mCameraCompatControlState = CAMERA_COMPAT_CONTROL_DISMISSED;
+ mCallback.onCameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED);
+ mLayout.setCameraControlVisibility(/* show= */ false);
+ }
- if (mLeash != null) {
- final SurfaceControl leash = mLeash;
- mSyncQueue.runInSync(t -> t.remove(leash));
- mLeash = null;
+ /** Called when the restart button is long clicked. */
+ void onRestartButtonLongClicked() {
+ if (mLayout == null) {
+ return;
}
+ mLayout.setSizeCompatHintVisibility(/* show= */ true);
}
- void relayout() {
- mViewHost.relayout(getWindowLayoutParams());
- updateSurfacePosition();
+ /** Called when either dismiss or treatment camera buttons is long clicked. */
+ void onCameraButtonLongClicked() {
+ if (mLayout == null) {
+ return;
+ }
+ mLayout.setCameraCompatHintVisibility(/* show= */ true);
}
- @VisibleForTesting
- void updateSurfacePosition() {
- if (mCompatUILayout == null || mLeash == null) {
+ @Override
+ protected void updateSurfacePosition(Rect taskBounds, Rect stableBounds) {
+ if (mLayout == null) {
return;
}
-
- // Use stable bounds to prevent controls from overlapping with system bars.
- final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
- final Rect stableBounds = new Rect();
- mDisplayLayout.getStableBounds(stableBounds);
- stableBounds.intersect(taskBounds);
-
// Position of the button in the container coordinate.
final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
? stableBounds.left - taskBounds.left
- : stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth();
+ : stableBounds.right - taskBounds.left - mLayout.getMeasuredWidth();
final int positionY = stableBounds.bottom - taskBounds.top
- - mCompatUILayout.getMeasuredHeight();
+ - mLayout.getMeasuredHeight();
updateSurfacePosition(positionX, positionY);
}
- private int getLayoutDirection() {
- return mContext.getResources().getConfiguration().getLayoutDirection();
- }
-
- private void updateSurfacePosition(int positionX, int positionY) {
- mSyncQueue.runInSync(t -> {
- if (mLeash == null || !mLeash.isValid()) {
- Log.w(TAG, "The leash has been released.");
- return;
- }
- t.setPosition(mLeash, positionX, positionY);
- // The compat UI should be the topmost child of the Task in case there can be more
- // than one children.
- t.setLayer(mLeash, Integer.MAX_VALUE);
- });
- }
-
- /** Inflates {@link CompatUILayout} on to the root surface. */
- private void initCompatUi() {
- if (mViewHost != null) {
- throw new IllegalStateException(
- "A UI has already been created with this window manager.");
+ private void updateVisibilityOfViews() {
+ if (mLayout == null) {
+ return;
+ }
+ // Size Compat mode restart button.
+ mLayout.setRestartButtonVisibility(mHasSizeCompat);
+ if (mHasSizeCompat && mShouldShowSizeCompatHint) {
+ mLayout.setSizeCompatHintVisibility(/* show= */ true);
+ // Only show by default for the first time.
+ mShouldShowSizeCompatHint = false;
}
- // Construction extracted into the separate methods to allow injection for tests.
- mViewHost = createSurfaceViewHost();
- mCompatUILayout = inflateCompatUILayout();
- mCompatUILayout.inject(this);
-
- mCompatUILayout.setSizeCompatHintVisibility(mShouldShowHint);
-
- mViewHost.setView(mCompatUILayout, getWindowLayoutParams());
-
- // Only show by default for the first time.
- mShouldShowHint = false;
- }
-
- @VisibleForTesting
- CompatUILayout inflateCompatUILayout() {
- return (CompatUILayout) LayoutInflater.from(mContext)
- .inflate(R.layout.compat_ui_layout, null);
- }
-
- @VisibleForTesting
- SurfaceControlViewHost createSurfaceViewHost() {
- return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ // Camera control for stretched issues.
+ mLayout.setCameraControlVisibility(shouldShowCameraControl());
+ if (shouldShowCameraControl() && mShouldShowCameraCompatHint) {
+ mLayout.setCameraCompatHintVisibility(/* show= */ true);
+ // Only show by default for the first time.
+ mShouldShowCameraCompatHint = false;
+ }
+ if (shouldShowCameraControl()) {
+ mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ }
}
- /** Gets the layout params. */
- private WindowManager.LayoutParams getWindowLayoutParams() {
- // Measure how big the hint is since its size depends on the text size.
- mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
- // Cannot be wrap_content as this determines the actual window size
- mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(),
- TYPE_APPLICATION_OVERLAY,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
- PixelFormat.TRANSLUCENT);
- winParams.token = new Binder();
- winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId);
- winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- return winParams;
+ private boolean shouldShowCameraControl() {
+ return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
+ && mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
}
-
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
new file mode 100644
index 000000000000..b9a9db1ee800
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -0,0 +1,364 @@
+/*
+ * 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.compatui;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+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_APPLICATION_OVERLAY;
+
+import android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.Log;
+import android.view.IWindow;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * A superclass for all Compat UI {@link WindowlessWindowManager}s that holds shared logic and
+ * exposes general API for {@link CompatUIController}.
+ *
+ * <p>Holds view hierarchy of a root surface and helps to inflate and manage layout.
+ */
+abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
+
+ protected final SyncTransactionQueue mSyncQueue;
+ protected final int mDisplayId;
+ protected final int mTaskId;
+
+ protected Context mContext;
+ protected Configuration mTaskConfig;
+ protected ShellTaskOrganizer.TaskListener mTaskListener;
+ protected DisplayLayout mDisplayLayout;
+ protected final Rect mStableBounds;
+
+ /**
+ * Utility class for adding and releasing a View hierarchy for this {@link
+ * WindowlessWindowManager} to {@code mLeash}.
+ */
+ @Nullable
+ protected SurfaceControlViewHost mViewHost;
+
+ /**
+ * A surface leash to position the layout relative to the task, since we can't set position for
+ * the {@code mViewHost} directly.
+ */
+ @Nullable
+ protected SurfaceControl mLeash;
+
+ protected CompatUIWindowManagerAbstract(Context context, Configuration taskConfig,
+ SyncTransactionQueue syncQueue, int taskId,
+ ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout) {
+ super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
+ mContext = context;
+ mSyncQueue = syncQueue;
+ mTaskConfig = taskConfig;
+ mDisplayId = mContext.getDisplayId();
+ mTaskId = taskId;
+ mTaskListener = taskListener;
+ mDisplayLayout = displayLayout;
+ mStableBounds = new Rect();
+ mDisplayLayout.getStableBounds(mStableBounds);
+ }
+
+ /**
+ * Returns the z-order of this window which will be passed to the {@link SurfaceControl} once
+ * {@link #attachToParentSurface} is called.
+ *
+ * <p>See {@link SurfaceControl.Transaction#setLayer}.
+ */
+ protected abstract int getZOrder();
+
+ /** Returns the layout of this window manager. */
+ protected abstract @Nullable View getLayout();
+
+ /**
+ * Inflates and inits the layout of this window manager on to the root surface if both {@code
+ * canShow} and {@link #eligibleToShowLayout} are true.
+ *
+ * @param canShow whether the layout is allowed to be shown by the parent controller.
+ */
+ void createLayout(boolean canShow) {
+ if (!canShow || !eligibleToShowLayout() || getLayout() != null) {
+ // Wait until layout should be visible.
+ return;
+ }
+
+ if (mViewHost != null) {
+ throw new IllegalStateException(
+ "A UI has already been created with this window manager.");
+ }
+
+ // Construction extracted into separate methods to allow injection for tests.
+ mViewHost = createSurfaceViewHost();
+ mViewHost.setView(createLayout(), getWindowLayoutParams());
+
+ updateSurfacePosition();
+ }
+
+ /** Inflates and inits the layout of this window manager. */
+ protected abstract View createLayout();
+
+ protected abstract void removeLayout();
+
+ /**
+ * Whether the layout is eligible to be shown according to the internal state of the subclass.
+ * Returns true by default if subclass doesn't override this method.
+ */
+ protected boolean eligibleToShowLayout() {
+ return true;
+ }
+
+ @Override
+ public void setConfiguration(Configuration configuration) {
+ super.setConfiguration(configuration);
+ mContext = mContext.createConfigurationContext(configuration);
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ String className = getClass().getSimpleName();
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName(className + "Leash")
+ .setHidden(false)
+ .setCallsite(className + "#attachToParentSurface");
+ attachToParentSurface(builder);
+ mLeash = builder.build();
+ b.setParent(mLeash);
+
+ initSurface(mLeash);
+ }
+
+ /** Inits the z-order of the surface. */
+ private void initSurface(SurfaceControl leash) {
+ final int z = getZOrder();
+ mSyncQueue.runInSync(t -> {
+ if (leash == null || !leash.isValid()) {
+ Log.w(getTag(), "The leash has been released.");
+ return;
+ }
+ t.setLayer(leash, z);
+ });
+ }
+
+ /**
+ * Called when compat info changed.
+ *
+ * @param canShow whether the layout is allowed to be shown by the parent controller.
+ */
+ void updateCompatInfo(TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
+ final Configuration prevTaskConfig = mTaskConfig;
+ final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
+ mTaskConfig = taskInfo.configuration;
+ mTaskListener = taskListener;
+
+ // Update configuration.
+ setConfiguration(mTaskConfig);
+
+ View layout = getLayout();
+ if (layout == null || prevTaskListener != taskListener) {
+ // TaskListener changed, recreate the layout for new surface parent.
+ release();
+ createLayout(canShow);
+ return;
+ }
+
+ boolean boundsUpdated = !mTaskConfig.windowConfiguration.getBounds().equals(
+ prevTaskConfig.windowConfiguration.getBounds());
+ boolean layoutDirectionUpdated =
+ mTaskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection();
+ if (boundsUpdated || layoutDirectionUpdated) {
+ // Reposition the UI surfaces.
+ updateSurfacePosition();
+ }
+
+ if (layout != null && layoutDirectionUpdated) {
+ // Update layout for RTL.
+ layout.setLayoutDirection(mTaskConfig.getLayoutDirection());
+ }
+ }
+
+
+ /**
+ * Updates the visibility of the layout.
+ *
+ * @param canShow whether the layout is allowed to be shown by the parent controller.
+ */
+ void updateVisibility(boolean canShow) {
+ View layout = getLayout();
+ if (layout == null) {
+ // Layout may not have been created because it was hidden previously.
+ createLayout(canShow);
+ return;
+ }
+
+ final int newVisibility = canShow && eligibleToShowLayout() ? View.VISIBLE : View.GONE;
+ if (layout.getVisibility() != newVisibility) {
+ layout.setVisibility(newVisibility);
+ }
+ }
+
+ /** Called when display layout changed. */
+ void updateDisplayLayout(DisplayLayout displayLayout) {
+ final Rect prevStableBounds = mStableBounds;
+ final Rect curStableBounds = new Rect();
+ displayLayout.getStableBounds(curStableBounds);
+ mDisplayLayout = displayLayout;
+ if (!prevStableBounds.equals(curStableBounds)) {
+ // Stable bounds changed, update UI surface positions.
+ updateSurfacePosition();
+ mStableBounds.set(curStableBounds);
+ }
+ }
+
+ /** Called when the surface is ready to be placed under the task surface. */
+ @VisibleForTesting
+ void attachToParentSurface(SurfaceControl.Builder b) {
+ mTaskListener.attachChildSurfaceToTask(mTaskId, b);
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ int getTaskId() {
+ return mTaskId;
+ }
+
+ /** Releases the surface control and tears down the view hierarchy. */
+ void release() {
+ // Hiding before releasing to avoid flickering when transitioning to the Home screen.
+ View layout = getLayout();
+ if (layout != null) {
+ layout.setVisibility(View.GONE);
+ }
+ removeLayout();
+
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+
+ if (mLeash != null) {
+ final SurfaceControl leash = mLeash;
+ mSyncQueue.runInSync(t -> t.remove(leash));
+ mLeash = null;
+ }
+ }
+
+ /** Re-layouts the view host and updates the surface position. */
+ void relayout() {
+ if (mViewHost == null) {
+ return;
+ }
+ mViewHost.relayout(getWindowLayoutParams());
+ updateSurfacePosition();
+ }
+
+ /**
+ * Updates the position of the surface with respect to the task bounds and display layout
+ * stable bounds.
+ */
+ @VisibleForTesting
+ void updateSurfacePosition() {
+ if (mLeash == null) {
+ return;
+ }
+ // Use stable bounds to prevent controls from overlapping with system bars.
+ final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
+ final Rect stableBounds = new Rect();
+ mDisplayLayout.getStableBounds(stableBounds);
+ stableBounds.intersect(taskBounds);
+
+ updateSurfacePosition(taskBounds, stableBounds);
+ }
+
+ /**
+ * Updates the position of the surface with respect to the given {@code taskBounds} and {@code
+ * stableBounds}.
+ */
+ protected abstract void updateSurfacePosition(Rect taskBounds, Rect stableBounds);
+
+ /**
+ * Updates the position of the surface with respect to the given {@code positionX} and {@code
+ * positionY}.
+ */
+ protected void updateSurfacePosition(int positionX, int positionY) {
+ mSyncQueue.runInSync(t -> {
+ if (mLeash == null || !mLeash.isValid()) {
+ Log.w(getTag(), "The leash has been released.");
+ return;
+ }
+ t.setPosition(mLeash, positionX, positionY);
+ });
+ }
+
+ protected int getLayoutDirection() {
+ return mContext.getResources().getConfiguration().getLayoutDirection();
+ }
+
+ @VisibleForTesting
+ SurfaceControlViewHost createSurfaceViewHost() {
+ return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ }
+
+ /** Gets the layout params. */
+ private WindowManager.LayoutParams getWindowLayoutParams() {
+ View layout = getLayout();
+ if (layout == null) {
+ return new WindowManager.LayoutParams();
+ }
+ // Measure how big the hint is since its size depends on the text size.
+ layout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ return getWindowLayoutParams(layout.getMeasuredWidth(), layout.getMeasuredHeight());
+ }
+
+ /** Gets the layout params given the width and height of the layout. */
+ private WindowManager.LayoutParams getWindowLayoutParams(int width, int height) {
+ final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+ // Cannot be wrap_content as this determines the actual window size
+ width, height,
+ TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ winParams.token = new Binder();
+ winParams.setTitle(getClass().getSimpleName() + mTaskId);
+ winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ return winParams;
+ }
+
+ protected final String getTag() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
new file mode 100644
index 000000000000..fc6fd3f2c959
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
@@ -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.
+ */
+
+package com.android.wm.shell.compatui.letterboxedu;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.wm.shell.R;
+
+/**
+ * Custom layout for Letterbox Education dialog action.
+ */
+// TODO(b/215316431): Add tests
+class LetterboxEduDialogActionLayout extends FrameLayout {
+
+ LetterboxEduDialogActionLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray styledAttributes =
+ context.getTheme().obtainStyledAttributes(
+ attrs,
+ R.styleable.LetterboxEduDialogActionLayout,
+ /* defStyleAttr= */ 0,
+ /* defStyleRes= */ 0);
+ int iconId = styledAttributes.getResourceId(
+ R.styleable.LetterboxEduDialogActionLayout_icon, 0);
+ String text = styledAttributes.getString(
+ R.styleable.LetterboxEduDialogActionLayout_text);
+ styledAttributes.recycle();
+
+ View rootView = inflate(getContext(), R.layout.letterbox_education_dialog_action_layout,
+ this);
+ ((ImageView) rootView.findViewById(
+ R.id.letterbox_education_dialog_action_icon)).setImageResource(iconId);
+ ((TextView) rootView.findViewById(R.id.letterbox_education_dialog_action_text)).setText(
+ text);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
new file mode 100644
index 000000000000..fa75d14b4aec
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
@@ -0,0 +1,75 @@
+/*
+ * 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.compatui.letterboxedu;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import com.android.wm.shell.R;
+
+/**
+ * Container for Letterbox Education Dialog and background dim.
+ *
+ * <p>This layout should fill the entire task and the background around the dialog acts as the
+ * background dim which dismisses the dialog when clicked.
+ */
+// TODO(b/215316431): Add tests
+class LetterboxEduDialogLayout extends FrameLayout {
+
+ // The alpha of a background is a number between 0 (fully transparent) to 255 (fully opaque).
+ // 204 is simply 255 * 0.8.
+ private static final int BACKGROUND_DIM_ALPHA = 204;
+
+ public LetterboxEduDialogLayout(Context context) {
+ this(context, null);
+ }
+
+ public LetterboxEduDialogLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public LetterboxEduDialogLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public LetterboxEduDialogLayout(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ /**
+ * Register a callback for the dismiss button and background dim.
+ *
+ * @param callback The callback to register
+ */
+ void setDismissOnClickListener(Runnable callback) {
+ findViewById(R.id.letterbox_education_dialog_dismiss_button).setOnClickListener(
+ view -> callback.run());
+ // Clicks on the background dim should also dismiss the dialog.
+ setOnClickListener(view -> callback.run());
+ // We add a no-op on-click listener to the dialog container so that clicks on it won't
+ // propagate to the listener of the layout (which represents the background dim).
+ findViewById(R.id.letterbox_education_dialog_container).setOnClickListener(view -> {});
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ getBackground().mutate().setAlpha(BACKGROUND_DIM_ALPHA);
+ }
+}
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 711a0ac76702..f91d7e2d28e8 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
@@ -30,7 +30,6 @@ import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSnapAlgorithm;
@@ -39,6 +38,7 @@ import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
@@ -61,7 +61,7 @@ public abstract class TvPipModule {
static Optional<Pip> providePip(
Context context,
PipBoundsState pipBoundsState,
- PipBoundsAlgorithm pipBoundsAlgorithm,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
TvPipMenuController tvPipMenuController,
PipMediaController pipMediaController,
@@ -74,7 +74,7 @@ public abstract class TvPipModule {
TvPipController.create(
context,
pipBoundsState,
- pipBoundsAlgorithm,
+ tvPipBoundsAlgorithm,
pipTaskOrganizer,
pipTransitionController,
tvPipMenuController,
@@ -93,9 +93,9 @@ public abstract class TvPipModule {
@WMSingleton
@Provides
- static PipBoundsAlgorithm providePipBoundsAlgorithm(Context context,
+ static TvPipBoundsAlgorithm provideTvPipBoundsAlgorithm(Context context,
PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
- return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
+ return new TvPipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
}
@WMSingleton
@@ -109,10 +109,11 @@ public abstract class TvPipModule {
@Provides
static PipTransitionController provideTvPipTransition(
Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
- PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipAnimationController pipAnimationController,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipBoundsState pipBoundsState, TvPipMenuController pipMenuController) {
return new TvPipTransition(pipBoundsState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
+ tvPipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
}
@WMSingleton
@@ -156,7 +157,7 @@ public abstract class TvPipModule {
SyncTransactionQueue syncTransactionQueue,
PipBoundsState pipBoundsState,
PipTransitionState pipTransitionState,
- PipBoundsAlgorithm pipBoundsAlgorithm,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
@@ -166,7 +167,7 @@ public abstract class TvPipModule {
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
- syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
+ syncTransactionQueue, pipTransitionState, pipBoundsState, tvPipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, newSplitScreenOptional,
displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
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 6d4b2fa60b55..2e54c792ed57 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
@@ -36,9 +36,12 @@ import com.android.wm.shell.ShellInitImpl;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.TaskViewFactoryController;
+import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.apppairs.AppPairsController;
+import com.android.wm.shell.back.BackAnimation;
+import com.android.wm.shell.back.BackAnimationController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
@@ -237,6 +240,17 @@ public abstract class WMShellBaseModule {
}
//
+ // Back animation
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<BackAnimation> provideBackAnimation(
+ Optional<BackAnimationController> backAnimationController) {
+ return backAnimationController.map(BackAnimationController::getBackAnimationImpl);
+ }
+
+ //
// Bubbles (optional feature)
//
@@ -253,15 +267,24 @@ public abstract class WMShellBaseModule {
// Fullscreen
//
+ // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract FullscreenTaskListener optionalFullscreenTaskListener();
+
@WMSingleton
@Provides
static FullscreenTaskListener provideFullscreenTaskListener(
+ @DynamicOverride Optional<FullscreenTaskListener> fullscreenTaskListener,
SyncTransactionQueue syncQueue,
Optional<FullscreenUnfoldController> optionalFullscreenUnfoldController,
- Optional<RecentTasksController> recentTasksOptional
- ) {
- return new FullscreenTaskListener(syncQueue, optionalFullscreenUnfoldController,
- recentTasksOptional);
+ Optional<RecentTasksController> recentTasksOptional) {
+ if (fullscreenTaskListener.isPresent()) {
+ return fullscreenTaskListener.get();
+ } else {
+ return new FullscreenTaskListener(syncQueue, optionalFullscreenUnfoldController,
+ recentTasksOptional);
+ }
}
//
@@ -352,7 +375,6 @@ public abstract class WMShellBaseModule {
return Optional.empty();
}
-
//
// Task to Surface communication
//
@@ -449,9 +471,16 @@ public abstract class WMShellBaseModule {
static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
DisplayController displayController, Context context,
@ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler,
@ShellAnimationThread ShellExecutor animExecutor) {
return new Transitions(organizer, pool, displayController, context, mainExecutor,
- animExecutor);
+ mainHandler, animExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static TaskViewTransitions provideTaskViewTransitions(Transitions transitions) {
+ return new TaskViewTransitions(transitions);
}
//
@@ -585,8 +614,10 @@ public abstract class WMShellBaseModule {
static TaskViewFactoryController provideTaskViewFactoryController(
ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor,
- SyncTransactionQueue syncQueue) {
- return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue);
+ SyncTransactionQueue syncQueue,
+ TaskViewTransitions taskViewTransitions) {
+ return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue,
+ taskViewTransitions);
}
//
@@ -661,4 +692,17 @@ public abstract class WMShellBaseModule {
legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
hideDisplayCutout, appPairsOptional, recentTasksOptional, mainExecutor);
}
+
+ @WMSingleton
+ @Provides
+ static Optional<BackAnimationController> provideBackAnimationController(
+ Context context,
+ @ShellMainThread ShellExecutor shellExecutor
+ ) {
+ if (BackAnimationController.IS_ENABLED) {
+ return Optional.of(
+ new BackAnimationController(shellExecutor, context));
+ }
+ return Optional.empty();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index f562fd9cf1af..7879e7a5bb00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -22,11 +22,13 @@ import android.content.pm.LauncherApps;
import android.os.Handler;
import android.view.WindowManager;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.bubbles.BubbleController;
@@ -106,13 +108,16 @@ public class WMShellModule {
UiEventLogger uiEventLogger,
ShellTaskOrganizer organizer,
DisplayController displayController,
+ @DynamicOverride Optional<OneHandedController> oneHandedOptional,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
+ TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
return BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
windowManagerShellWrapper, launcherApps, taskStackListener,
- uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue);
+ uiEventLogger, organizer, displayController, oneHandedOptional,
+ mainExecutor, mainHandler, taskViewTransitions, syncQueue);
}
//
@@ -139,12 +144,10 @@ public class WMShellModule {
static OneHandedController provideOneHandedController(Context context,
WindowManager windowManager, DisplayController displayController,
DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
- return OneHandedController.create(context, windowManager,
- displayController, displayLayout, taskStackListener, uiEventLogger, mainExecutor,
- mainHandler);
+ UiEventLogger uiEventLogger, InteractionJankMonitor jankMonitor,
+ @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) {
+ return OneHandedController.create(context, windowManager, displayController, displayLayout,
+ taskStackListener, jankMonitor, uiEventLogger, mainExecutor, mainHandler);
}
//
@@ -159,13 +162,14 @@ public class WMShellModule {
SyncTransactionQueue syncQueue, Context context,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
@ShellMainThread ShellExecutor mainExecutor,
+ DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
return new SplitScreenController(shellTaskOrganizer, syncQueue, context,
- rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
+ rootTaskDisplayAreaOrganizer, mainExecutor, displayController, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
recentTasks, stageTaskUnfoldControllerProvider);
}
@@ -242,10 +246,11 @@ public class WMShellModule {
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
SystemWindows systemWindows,
Optional<SplitScreenController> splitScreenOptional,
+ PipUiEventLogger pipUiEventLogger,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
- systemWindows, splitScreenOptional, mainExecutor, mainHandler);
+ systemWindows, splitScreenOptional, pipUiEventLogger, mainExecutor, mainHandler);
}
@WMSingleton
@@ -305,9 +310,12 @@ public class WMShellModule {
Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
- PhonePipMenuController pipMenuController) {
+ PhonePipMenuController pipMenuController,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ Optional<SplitScreenController> splitScreenOptional) {
return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
+ pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer,
+ pipSurfaceTransactionHelper, splitScreenOptional);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 8e6c05d83906..5ebdceba135b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -62,6 +62,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.InstanceId;
+import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -132,6 +133,8 @@ public class DragAndDropPolicy {
final Rect fullscreenHitRegion = new Rect(displayRegion);
final boolean inLandscape = mSession.displayLayout.isLandscape();
final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
+ final float dividerWidth = mContext.getResources().getDimensionPixelSize(
+ R.dimen.split_divider_bar_width);
// We allow splitting if we are already in split-screen or the running task is a standard
// task in fullscreen mode.
final boolean allowSplit = inSplitScreen
@@ -147,37 +150,45 @@ public class DragAndDropPolicy {
if (inLandscape) {
final Rect leftHitRegion = new Rect();
- final Rect leftDrawRegion = topOrLeftBounds;
final Rect rightHitRegion = new Rect();
- final Rect rightDrawRegion = bottomOrRightBounds;
// If we have existing split regions use those bounds, otherwise split it 50/50
if (inSplitScreen) {
- leftHitRegion.set(topOrLeftBounds);
- rightHitRegion.set(bottomOrRightBounds);
+ // The bounds of the existing split will have a divider bar, the hit region
+ // should include that space. Find the center of the divider bar:
+ float centerX = topOrLeftBounds.right + (dividerWidth / 2);
+ // Now set the hit regions using that center.
+ leftHitRegion.set(displayRegion);
+ leftHitRegion.right = (int) centerX;
+ rightHitRegion.set(displayRegion);
+ rightHitRegion.left = (int) centerX;
} else {
displayRegion.splitVertically(leftHitRegion, rightHitRegion);
}
- mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, leftDrawRegion));
- mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, rightDrawRegion));
+ mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds));
+ mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, bottomOrRightBounds));
} else {
final Rect topHitRegion = new Rect();
- final Rect topDrawRegion = topOrLeftBounds;
final Rect bottomHitRegion = new Rect();
- final Rect bottomDrawRegion = bottomOrRightBounds;
// If we have existing split regions use those bounds, otherwise split it 50/50
if (inSplitScreen) {
- topHitRegion.set(topOrLeftBounds);
- bottomHitRegion.set(bottomOrRightBounds);
+ // The bounds of the existing split will have a divider bar, the hit region
+ // should include that space. Find the center of the divider bar:
+ float centerX = topOrLeftBounds.bottom + (dividerWidth / 2);
+ // Now set the hit regions using that center.
+ topHitRegion.set(displayRegion);
+ topHitRegion.bottom = (int) centerX;
+ bottomHitRegion.set(displayRegion);
+ bottomHitRegion.top = (int) centerX;
} else {
displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
}
- mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topDrawRegion));
- mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomDrawRegion));
+ mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds));
+ mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomOrRightBounds));
}
} else {
// Split-screen not allowed, so only show the fullscreen target
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index f98849260511..7307ba30fd67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -24,7 +24,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -154,10 +153,6 @@ public class DragLayout extends LinearLayout {
}
}
- public boolean hasDropTarget() {
- return mCurrentTarget != null;
- }
-
public boolean hasDropped() {
return mHasDropped;
}
@@ -269,6 +264,9 @@ public class DragLayout extends LinearLayout {
* Updates the visible drop target as the user drags.
*/
public void update(DragEvent event) {
+ if (mHasDropped) {
+ return;
+ }
// Find containing region, if the same as mCurrentRegion, then skip, otherwise, animate the
// visibility of the current region
DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation(
@@ -284,7 +282,8 @@ public class DragLayout extends LinearLayout {
animateHighlight(target);
} else {
// Switching between targets
- animateHighlight(target);
+ mDropZoneView1.animateSwitch();
+ mDropZoneView2.animateSwitch();
}
mCurrentTarget = target;
}
@@ -321,7 +320,7 @@ public class DragLayout extends LinearLayout {
: DISABLE_NONE);
mDropZoneView1.setShowingMargin(visible);
mDropZoneView2.setShowingMargin(visible);
- ObjectAnimator animator = mDropZoneView1.getAnimator();
+ Animator animator = mDropZoneView1.getAnimator();
if (animCompleteCallback != null) {
if (animator != null) {
animator.addListener(new AnimatorListenerAdapter() {
@@ -341,17 +340,11 @@ public class DragLayout extends LinearLayout {
if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_LEFT
|| target.type == DragAndDropPolicy.Target.TYPE_SPLIT_TOP) {
mDropZoneView1.setShowingHighlight(true);
- mDropZoneView1.setShowingSplash(false);
-
mDropZoneView2.setShowingHighlight(false);
- mDropZoneView2.setShowingSplash(true);
} else if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT
|| target.type == DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM) {
mDropZoneView1.setShowingHighlight(false);
- mDropZoneView1.setShowingSplash(true);
-
mDropZoneView2.setShowingHighlight(true);
- mDropZoneView2.setShowingSplash(false);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
index 2f47af57d496..a3ee8aed204d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.draganddrop;
import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN;
+import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
@@ -27,7 +28,6 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.FloatProperty;
-import android.util.IntProperty;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -43,8 +43,8 @@ import com.android.wm.shell.R;
*/
public class DropZoneView extends FrameLayout {
- private static final int SPLASHSCREEN_ALPHA_INT = (int) (255 * 0.90f);
- private static final int HIGHLIGHT_ALPHA_INT = 255;
+ private static final float SPLASHSCREEN_ALPHA = 0.90f;
+ private static final float HIGHLIGHT_ALPHA = 1f;
private static final int MARGIN_ANIMATION_ENTER_DURATION = 400;
private static final int MARGIN_ANIMATION_EXIT_DURATION = 250;
@@ -61,54 +61,27 @@ public class DropZoneView extends FrameLayout {
}
};
- private static final IntProperty<ColorDrawable> SPLASHSCREEN_ALPHA =
- new IntProperty<ColorDrawable>("splashscreen") {
- @Override
- public void setValue(ColorDrawable d, int alpha) {
- d.setAlpha(alpha);
- }
-
- @Override
- public Integer get(ColorDrawable d) {
- return d.getAlpha();
- }
- };
-
- private static final IntProperty<ColorDrawable> HIGHLIGHT_ALPHA =
- new IntProperty<ColorDrawable>("highlight") {
- @Override
- public void setValue(ColorDrawable d, int alpha) {
- d.setAlpha(alpha);
- }
-
- @Override
- public Integer get(ColorDrawable d) {
- return d.getAlpha();
- }
- };
-
private final Path mPath = new Path();
private final float[] mContainerMargin = new float[4];
private float mCornerRadius;
private float mBottomInset;
private int mMarginColor; // i.e. color used for negative space like the container insets
- private int mHighlightColor;
private boolean mShowingHighlight;
private boolean mShowingSplash;
private boolean mShowingMargin;
- // TODO: might be more seamless to animate between splash/highlight color instead of 2 separate
- private ObjectAnimator mSplashAnimator;
- private ObjectAnimator mHighlightAnimator;
+ private int mSplashScreenColor;
+ private int mHighlightColor;
+
+ private ObjectAnimator mBackgroundAnimator;
private ObjectAnimator mMarginAnimator;
private float mMarginPercent;
// Renders a highlight or neutral transparent color
- private ColorDrawable mDropZoneDrawable;
+ private ColorDrawable mColorDrawable;
// Renders the translucent splashscreen with the app icon in the middle
private ImageView mSplashScreenView;
- private ColorDrawable mSplashBackgroundDrawable;
// Renders the margin / insets around the dropzone container
private MarginView mMarginView;
@@ -130,19 +103,14 @@ public class DropZoneView extends FrameLayout {
mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
mMarginColor = getResources().getColor(R.color.taskbar_background);
- mHighlightColor = getResources().getColor(android.R.color.system_accent1_500);
-
- mDropZoneDrawable = new ColorDrawable();
- mDropZoneDrawable.setColor(mHighlightColor);
- mDropZoneDrawable.setAlpha(0);
- setBackgroundDrawable(mDropZoneDrawable);
+ int c = getResources().getColor(android.R.color.system_accent1_500);
+ mHighlightColor = Color.argb(HIGHLIGHT_ALPHA, Color.red(c), Color.green(c), Color.blue(c));
+ mSplashScreenColor = Color.argb(SPLASHSCREEN_ALPHA, 0, 0, 0);
+ mColorDrawable = new ColorDrawable();
+ setBackgroundDrawable(mColorDrawable);
mSplashScreenView = new ImageView(context);
mSplashScreenView.setScaleType(ImageView.ScaleType.CENTER);
- mSplashBackgroundDrawable = new ColorDrawable();
- mSplashBackgroundDrawable.setColor(Color.WHITE);
- mSplashBackgroundDrawable.setAlpha(SPLASHSCREEN_ALPHA_INT);
- mSplashScreenView.setBackgroundDrawable(mSplashBackgroundDrawable);
addView(mSplashScreenView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mSplashScreenView.setAlpha(0f);
@@ -157,10 +125,6 @@ public class DropZoneView extends FrameLayout {
mMarginColor = getResources().getColor(R.color.taskbar_background);
mHighlightColor = getResources().getColor(android.R.color.system_accent1_500);
- final int alpha = mDropZoneDrawable.getAlpha();
- mDropZoneDrawable.setColor(mHighlightColor);
- mDropZoneDrawable.setAlpha(alpha);
-
if (mMarginPercent > 0) {
mMarginView.invalidate();
}
@@ -187,38 +151,39 @@ public class DropZoneView extends FrameLayout {
}
/** Sets the color and icon to use for the splashscreen when shown. */
- public void setAppInfo(int splashScreenColor, Drawable appIcon) {
- mSplashBackgroundDrawable.setColor(splashScreenColor);
+ public void setAppInfo(int color, Drawable appIcon) {
+ Color c = Color.valueOf(color);
+ mSplashScreenColor = Color.argb(SPLASHSCREEN_ALPHA, c.red(), c.green(), c.blue());
mSplashScreenView.setImageDrawable(appIcon);
}
/** @return an active animator for this view if one exists. */
@Nullable
- public ObjectAnimator getAnimator() {
+ public Animator getAnimator() {
if (mMarginAnimator != null && mMarginAnimator.isRunning()) {
return mMarginAnimator;
- } else if (mHighlightAnimator != null && mHighlightAnimator.isRunning()) {
- return mHighlightAnimator;
- } else if (mSplashAnimator != null && mSplashAnimator.isRunning()) {
- return mSplashAnimator;
+ } else if (mBackgroundAnimator != null && mBackgroundAnimator.isRunning()) {
+ return mBackgroundAnimator;
}
return null;
}
- /** Animates the splashscreen to show or hide. */
- public void setShowingSplash(boolean showingSplash) {
- if (mShowingSplash != showingSplash) {
- mShowingSplash = showingSplash;
- animateSplashToState();
- }
+ /** Animates between highlight and splashscreen depending on current state. */
+ public void animateSwitch() {
+ mShowingHighlight = !mShowingHighlight;
+ mShowingSplash = !mShowingHighlight;
+ final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor;
+ animateBackground(mColorDrawable.getColor(), newColor);
+ animateSplashScreenIcon();
}
/** Animates the highlight indicating the zone is hovered on or not. */
public void setShowingHighlight(boolean showingHighlight) {
- if (mShowingHighlight != showingHighlight) {
- mShowingHighlight = showingHighlight;
- animateHighlightToState();
- }
+ mShowingHighlight = showingHighlight;
+ mShowingSplash = !mShowingHighlight;
+ final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor;
+ animateBackground(Color.TRANSPARENT, newColor);
+ animateSplashScreenIcon();
}
/** Animates the margins around the drop zone to show or hide. */
@@ -228,38 +193,29 @@ public class DropZoneView extends FrameLayout {
animateMarginToState();
}
if (!mShowingMargin) {
- setShowingHighlight(false);
- setShowingSplash(false);
+ mShowingHighlight = false;
+ mShowingSplash = false;
+ animateBackground(mColorDrawable.getColor(), Color.TRANSPARENT);
+ animateSplashScreenIcon();
}
}
- private void animateSplashToState() {
- if (mSplashAnimator != null) {
- mSplashAnimator.cancel();
+ private void animateBackground(int startColor, int endColor) {
+ if (mBackgroundAnimator != null) {
+ mBackgroundAnimator.cancel();
}
- mSplashAnimator = ObjectAnimator.ofInt(mSplashBackgroundDrawable,
- SPLASHSCREEN_ALPHA,
- mSplashBackgroundDrawable.getAlpha(),
- mShowingSplash ? SPLASHSCREEN_ALPHA_INT : 0);
- if (!mShowingSplash) {
- mSplashAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+ mBackgroundAnimator = ObjectAnimator.ofArgb(mColorDrawable,
+ "color",
+ startColor,
+ endColor);
+ if (!mShowingSplash && !mShowingHighlight) {
+ mBackgroundAnimator.setInterpolator(FAST_OUT_SLOW_IN);
}
- mSplashAnimator.start();
- mSplashScreenView.animate().alpha(mShowingSplash ? 1f : 0f).start();
+ mBackgroundAnimator.start();
}
- private void animateHighlightToState() {
- if (mHighlightAnimator != null) {
- mHighlightAnimator.cancel();
- }
- mHighlightAnimator = ObjectAnimator.ofInt(mDropZoneDrawable,
- HIGHLIGHT_ALPHA,
- mDropZoneDrawable.getAlpha(),
- mShowingHighlight ? HIGHLIGHT_ALPHA_INT : 0);
- if (!mShowingHighlight) {
- mHighlightAnimator.setInterpolator(FAST_OUT_SLOW_IN);
- }
- mHighlightAnimator.start();
+ private void animateSplashScreenIcon() {
+ mSplashScreenView.animate().alpha(mShowingSplash ? 1f : 0f).start();
}
private void animateMarginToState() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
new file mode 100644
index 000000000000..c20b7d9b2747
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
@@ -0,0 +1,246 @@
+/*
+ * 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.onehanded;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.Slog;
+import android.view.ContextThemeWrapper;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayLayout;
+
+import java.io.PrintWriter;
+
+/**
+ * Holds view hierarchy of a root surface and helps inflate a themeable view for background.
+ */
+public final class BackgroundWindowManager extends WindowlessWindowManager {
+ private static final String TAG = BackgroundWindowManager.class.getSimpleName();
+ private static final int THEME_COLOR_OFFSET = 10;
+
+ private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ mTransactionFactory;
+
+ private Context mContext;
+ private Rect mDisplayBounds;
+ private SurfaceControlViewHost mViewHost;
+ private SurfaceControl mLeash;
+ private View mBackgroundView;
+ private @OneHandedState.State int mCurrentState;
+
+ public BackgroundWindowManager(Context context) {
+ super(context.getResources().getConfiguration(), null /* rootSurface */,
+ null /* hostInputToken */);
+ mContext = context;
+ mTransactionFactory = SurfaceControl.Transaction::new;
+ }
+
+ @Override
+ public SurfaceControl getSurfaceControl(IWindow window) {
+ return super.getSurfaceControl(window);
+ }
+
+ @Override
+ public void setConfiguration(Configuration configuration) {
+ super.setConfiguration(configuration);
+ mContext = mContext.createConfigurationContext(configuration);
+ }
+
+ /**
+ * onConfigurationChanged events for updating background theme color.
+ */
+ public void onConfigurationChanged() {
+ if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
+ updateThemeOnly();
+ }
+ }
+
+ /**
+ * One-handed mode state changed callback
+ * @param newState of One-handed mode representing by {@link OneHandedState}
+ */
+ public void onStateChanged(int newState) {
+ mCurrentState = newState;
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setColorLayer()
+ .setBufferSize(mDisplayBounds.width(), mDisplayBounds.height())
+ .setFormat(PixelFormat.RGB_888)
+ .setOpaque(true)
+ .setName(TAG)
+ .setCallsite("BackgroundWindowManager#attachToParentSurface");
+ mLeash = builder.build();
+ b.setParent(mLeash);
+ }
+
+ /** Inflates background view on to the root surface. */
+ boolean initView() {
+ if (mBackgroundView != null || mViewHost != null) {
+ return false;
+ }
+
+ mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ mBackgroundView = (View) LayoutInflater.from(mContext)
+ .inflate(R.layout.background_panel, null /* root */);
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ mDisplayBounds.width(), mDisplayBounds.height(), 0 /* TYPE NONE */,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH
+ | FLAG_SLIPPERY, PixelFormat.TRANSLUCENT);
+ lp.token = new Binder();
+ lp.setTitle("background-panel");
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ mBackgroundView.setBackgroundColor(getThemeColorForBackground());
+ mViewHost.setView(mBackgroundView, lp);
+ return true;
+ }
+
+ /**
+ * Called when onDisplayAdded() or onDisplayRemoved() callback.
+ * @param displayLayout The latest {@link DisplayLayout} for display bounds.
+ */
+ public void onDisplayChanged(DisplayLayout displayLayout) {
+ // One-handed mode is only available on portrait.
+ if (displayLayout.height() > displayLayout.width()) {
+ mDisplayBounds = new Rect(0, 0, displayLayout.width(), displayLayout.height());
+ } else {
+ mDisplayBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width());
+ }
+ }
+
+ private void updateThemeOnly() {
+ if (mBackgroundView == null || mViewHost == null || mLeash == null) {
+ Slog.w(TAG, "Background view or SurfaceControl does not exist when trying to "
+ + "update theme only!");
+ return;
+ }
+
+ WindowManager.LayoutParams lp = (WindowManager.LayoutParams)
+ mBackgroundView.getLayoutParams();
+ mBackgroundView.setBackgroundColor(getThemeColorForBackground());
+ mViewHost.setView(mBackgroundView, lp);
+ }
+
+ /**
+ * Shows the background layer when One-handed mode triggered.
+ */
+ public void showBackgroundLayer() {
+ if (!initView()) {
+ updateThemeOnly();
+ return;
+ }
+ if (mLeash == null) {
+ Slog.w(TAG, "SurfaceControl mLeash is null, can't show One-handed mode "
+ + "background panel!");
+ return;
+ }
+
+ mTransactionFactory.getTransaction()
+ .setAlpha(mLeash, 1.0f)
+ .setLayer(mLeash, -1 /* at bottom-most layer */)
+ .show(mLeash)
+ .apply();
+ }
+
+ /**
+ * Remove the leash of background layer after stop One-handed mode.
+ */
+ public void removeBackgroundLayer() {
+ if (mBackgroundView != null) {
+ mBackgroundView = null;
+ }
+
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+
+ if (mLeash != null) {
+ mTransactionFactory.getTransaction().remove(mLeash).apply();
+ mLeash = null;
+ }
+ }
+
+ /**
+ * Gets {@link SurfaceControl} of the background layer.
+ * @return {@code null} if not exist.
+ */
+ @Nullable
+ SurfaceControl getSurfaceControl() {
+ return mLeash;
+ }
+
+ private int getThemeColor() {
+ final Context themedContext = new ContextThemeWrapper(mContext,
+ com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+ return themedContext.getColor(R.color.one_handed_tutorial_background_color);
+ }
+
+ int getThemeColorForBackground() {
+ final int origThemeColor = getThemeColor();
+ return android.graphics.Color.argb(Color.alpha(origThemeColor),
+ Color.red(origThemeColor) - THEME_COLOR_OFFSET,
+ Color.green(origThemeColor) - THEME_COLOR_OFFSET,
+ Color.blue(origThemeColor) - THEME_COLOR_OFFSET);
+ }
+
+ private float adjustColor(int origColor) {
+ return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f;
+ }
+
+ void dump(@NonNull PrintWriter pw) {
+ final String innerPrefix = " ";
+ pw.println(TAG);
+ pw.print(innerPrefix + "mDisplayBounds=");
+ pw.println(mDisplayBounds);
+ pw.print(innerPrefix + "mViewHost=");
+ pw.println(mViewHost);
+ pw.print(innerPrefix + "mLeash=");
+ pw.println(mLeash);
+ pw.print(innerPrefix + "mBackgroundView=");
+ pw.println(mBackgroundView);
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index 3253bb06c835..b00182f36cc8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.onehanded;
import android.content.res.Configuration;
+import android.os.SystemProperties;
import com.android.wm.shell.common.annotations.ExternalThread;
@@ -26,6 +27,9 @@ import com.android.wm.shell.common.annotations.ExternalThread;
@ExternalThread
public interface OneHanded {
+ boolean sIsSupportOneHandedMode = SystemProperties.getBoolean(
+ OneHandedController.SUPPORT_ONE_HANDED_MODE, false);
+
/**
* Returns a binder that can be passed to an external process to manipulate OneHanded.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
deleted file mode 100644
index 9e1c61aac868..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
+++ /dev/null
@@ -1,272 +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.onehanded;
-
-import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.view.ContextThemeWrapper;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.view.animation.LinearInterpolator;
-import android.window.DisplayAreaAppearedInfo;
-import android.window.DisplayAreaInfo;
-import android.window.DisplayAreaOrganizer;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayLayout;
-
-import java.io.PrintWriter;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Manages OneHanded color background layer areas.
- * To avoid when turning the Dark theme on, users can not clearly identify
- * the screen has entered one handed mode.
- */
-public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer
- implements OneHandedAnimationCallback, OneHandedState.OnStateChangedListener {
- private static final String TAG = "OneHandedBackgroundPanelOrganizer";
- private static final int THEME_COLOR_OFFSET = 10;
- private static final int ALPHA_ANIMATION_DURATION = 200;
-
- private final Context mContext;
- private final SurfaceSession mSurfaceSession = new SurfaceSession();
- private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
- mTransactionFactory;
-
- private @OneHandedState.State int mCurrentState;
- private ValueAnimator mAlphaAnimator;
-
- private float mTranslationFraction;
- private float[] mThemeColor;
-
- /**
- * The background to distinguish the boundary of translated windows and empty region when
- * one handed mode triggered.
- */
- private Rect mBkgBounds;
- private Rect mStableInsets;
-
- @Nullable
- @VisibleForTesting
- SurfaceControl mBackgroundSurface;
- @Nullable
- private SurfaceControl mParentLeash;
-
- public OneHandedBackgroundPanelOrganizer(Context context, DisplayLayout displayLayout,
- OneHandedSettingsUtil settingsUtil, Executor executor) {
- super(executor);
- mContext = context;
- mTranslationFraction = settingsUtil.getTranslationFraction(context);
- mTransactionFactory = SurfaceControl.Transaction::new;
- updateThemeColors();
- }
-
- @Override
- public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
- @NonNull SurfaceControl leash) {
- mParentLeash = leash;
- }
-
- @Override
- public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) {
- final List<DisplayAreaAppearedInfo> displayAreaInfos;
- displayAreaInfos = super.registerOrganizer(displayAreaFeature);
- for (int i = 0; i < displayAreaInfos.size(); i++) {
- final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
- onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
- }
- return displayAreaInfos;
- }
-
- @Override
- public void unregisterOrganizer() {
- super.unregisterOrganizer();
- removeBackgroundPanelLayer();
- mParentLeash = null;
- }
-
- @Override
- public void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) {
- final int yTopPos = (mStableInsets.top - mBkgBounds.height()) + Math.round(yPos);
- tx.setPosition(mBackgroundSurface, 0, yTopPos);
- }
-
- @Nullable
- @VisibleForTesting
- boolean isRegistered() {
- return mParentLeash != null;
- }
-
- void createBackgroundSurface() {
- mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession)
- .setBufferSize(mBkgBounds.width(), mBkgBounds.height())
- .setColorLayer()
- .setFormat(PixelFormat.RGB_888)
- .setOpaque(true)
- .setName("one-handed-background-panel")
- .setCallsite("OneHandedBackgroundPanelOrganizer")
- .build();
-
- // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
- mAlphaAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
- mAlphaAnimator.setInterpolator(new LinearInterpolator());
- mAlphaAnimator.setDuration(ALPHA_ANIMATION_DURATION);
- mAlphaAnimator.addUpdateListener(
- animator -> detachBackgroundFromParent(animator));
- }
-
- void detachBackgroundFromParent(ValueAnimator animator) {
- if (mBackgroundSurface == null || mParentLeash == null) {
- return;
- }
- // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
- final float currentValue = (float) animator.getAnimatedValue();
- final SurfaceControl.Transaction tx = mTransactionFactory.getTransaction();
- if (currentValue == 0.0f) {
- tx.reparent(mBackgroundSurface, null).apply();
- } else {
- tx.setAlpha(mBackgroundSurface, (float) animator.getAnimatedValue()).apply();
- }
- }
-
- /**
- * Called when onDisplayAdded() or onDisplayRemoved() callback.
- *
- * @param displayLayout The latest {@link DisplayLayout} representing current displayId
- */
- public void onDisplayChanged(DisplayLayout displayLayout) {
- mStableInsets = displayLayout.stableInsets();
- // Ensure the mBkgBounds is portrait, due to OHM only support on portrait
- if (displayLayout.height() > displayLayout.width()) {
- mBkgBounds = new Rect(0, 0, displayLayout.width(),
- Math.round(displayLayout.height() * mTranslationFraction) + mStableInsets.top);
- } else {
- mBkgBounds = new Rect(0, 0, displayLayout.height(),
- Math.round(displayLayout.width() * mTranslationFraction) + mStableInsets.top);
- }
- }
-
- @VisibleForTesting
- void onStart() {
- if (mBackgroundSurface == null) {
- createBackgroundSurface();
- }
- showBackgroundPanelLayer();
- }
-
- /**
- * Called when transition finished.
- */
- public void onStopFinished() {
- if (mAlphaAnimator == null) {
- return;
- }
- mAlphaAnimator.start();
- }
-
- @VisibleForTesting
- void showBackgroundPanelLayer() {
- if (mParentLeash == null) {
- return;
- }
-
- if (mBackgroundSurface == null) {
- createBackgroundSurface();
- }
-
- // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
- if (mAlphaAnimator.isRunning()) {
- mAlphaAnimator.end();
- }
-
- mTransactionFactory.getTransaction()
- .reparent(mBackgroundSurface, mParentLeash)
- .setAlpha(mBackgroundSurface, 1.0f)
- .setLayer(mBackgroundSurface, -1 /* at bottom-most layer */)
- .setColor(mBackgroundSurface, mThemeColor)
- .show(mBackgroundSurface)
- .apply();
- }
-
- @VisibleForTesting
- void removeBackgroundPanelLayer() {
- if (mBackgroundSurface == null) {
- return;
- }
-
- mTransactionFactory.getTransaction()
- .remove(mBackgroundSurface)
- .apply();
- mBackgroundSurface = null;
- }
-
- /**
- * onConfigurationChanged events for updating tutorial text.
- */
- public void onConfigurationChanged() {
- updateThemeColors();
-
- if (mCurrentState != STATE_ACTIVE) {
- return;
- }
- showBackgroundPanelLayer();
- }
-
- private void updateThemeColors() {
- final Context themedContext = new ContextThemeWrapper(mContext,
- com.android.internal.R.style.Theme_DeviceDefault_DayNight);
- final int themeColor = themedContext.getColor(
- R.color.one_handed_tutorial_background_color);
- mThemeColor = new float[]{
- adjustColor(Color.red(themeColor)),
- adjustColor(Color.green(themeColor)),
- adjustColor(Color.blue(themeColor))};
- }
-
- private float adjustColor(int origColor) {
- return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f;
- }
-
- @Override
- public void onStateChanged(int newState) {
- mCurrentState = newState;
- }
-
- void dump(@NonNull PrintWriter pw) {
- final String innerPrefix = " ";
- pw.println(TAG);
- pw.print(innerPrefix + "mBackgroundSurface=");
- pw.println(mBackgroundSurface);
- pw.print(innerPrefix + "mBkgBounds=");
- pw.println(mBkgBounds);
- pw.print(innerPrefix + "mThemeColor=");
- pw.println(mThemeColor);
- pw.print(innerPrefix + "mTranslationFraction=");
- pw.println(mTranslationFraction);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index e0686146e821..48acfc1c76e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.onehanded;
-import static android.os.UserHandle.USER_CURRENT;
import static android.os.UserHandle.myUserId;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -30,12 +29,10 @@ import android.annotation.BinderThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.om.IOverlayManager;
-import android.content.om.OverlayInfo;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -48,6 +45,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayChangeController;
@@ -70,9 +68,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
"persist.debug.one_handed_offset_percentage";
- private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY =
- "com.android.internal.systemui.onehanded.gestural";
- private static final int OVERLAY_ENABLED_DELAY_MS = 250;
private static final int DISPLAY_AREA_READY_RETRY_MS = 10;
public static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
@@ -104,7 +99,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
private OneHandedEventCallback mEventCallback;
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
- private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
private OneHandedUiEventLogger mOneHandedUiEventLogger;
private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
@@ -168,7 +162,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
public void onStopFinished(Rect bounds) {
mState.setState(STATE_NONE);
notifyShortcutStateChanged(STATE_NONE);
- mBackgroundPanelOrganizer.onStopFinished();
}
};
@@ -200,37 +193,34 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
public static OneHandedController create(
Context context, WindowManager windowManager, DisplayController displayController,
DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger, ShellExecutor mainExecutor, Handler mainHandler) {
+ InteractionJankMonitor jankMonitor, UiEventLogger uiEventLogger,
+ ShellExecutor mainExecutor, Handler mainHandler) {
OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
- OneHandedState transitionState = new OneHandedState();
+ OneHandedState oneHandedState = new OneHandedState();
+ BackgroundWindowManager backgroundWindowManager = new BackgroundWindowManager(context);
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
- settingsUtil, windowManager);
+ settingsUtil, windowManager, backgroundWindowManager);
OneHandedAnimationController animationController =
new OneHandedAnimationController(context);
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
mainExecutor);
- OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
- new OneHandedBackgroundPanelOrganizer(context, displayLayout, settingsUtil,
- mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
context, displayLayout, settingsUtil, animationController, tutorialHandler,
- oneHandedBackgroundPanelOrganizer, mainExecutor);
+ jankMonitor, mainExecutor);
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
- return new OneHandedController(context, displayController,
- oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
- settingsUtil, accessibilityUtil, timeoutHandler, transitionState,
- oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
- mainHandler);
+ return new OneHandedController(context, displayController, organizer, touchHandler,
+ tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState,
+ jankMonitor, oneHandedUiEventsLogger, overlayManager, taskStackListener,
+ mainExecutor, mainHandler);
}
@VisibleForTesting
OneHandedController(Context context,
DisplayController displayController,
- OneHandedBackgroundPanelOrganizer backgroundPanelOrganizer,
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
OneHandedTouchHandler touchHandler,
OneHandedTutorialHandler tutorialHandler,
@@ -238,6 +228,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
OneHandedTimeoutHandler timeoutHandler,
OneHandedState state,
+ InteractionJankMonitor jankMonitor,
OneHandedUiEventLogger uiEventsLogger,
IOverlayManager overlayManager,
TaskStackListenerImpl taskStackListener,
@@ -246,7 +237,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mContext = context;
mOneHandedSettingsUtil = settingsUtil;
mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil;
- mBackgroundPanelOrganizer = backgroundPanelOrganizer;
mDisplayAreaOrganizer = displayAreaOrganizer;
mDisplayController = displayController;
mTouchHandler = touchHandler;
@@ -282,14 +272,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
setupCallback();
registerSettingObservers(mUserId);
setupTimeoutListener();
- setupGesturalOverlay();
updateSettings();
+ updateDisplayLayout(mContext.getDisplayId());
mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
- mState.addSListeners(mBackgroundPanelOrganizer);
mState.addSListeners(mTutorialHandler);
}
@@ -371,7 +360,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
mOneHandedAccessibilityUtil.announcementForScreenReader(
mOneHandedAccessibilityUtil.getOneHandedStartDescription());
- mBackgroundPanelOrganizer.onStart();
mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
mTimeoutHandler.resetTimer();
mOneHandedUiEventLogger.writeEvent(
@@ -399,8 +387,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mEventCallback = callback;
}
- @VisibleForTesting
- void registerTransitionCallback(OneHandedTransitionCallback callback) {
+ /**
+ * Registers {@link OneHandedTransitionCallback} to monitor the transition status
+ */
+ public void registerTransitionCallback(OneHandedTransitionCallback callback) {
mDisplayAreaOrganizer.registerTransitionCallback(callback);
}
@@ -453,11 +443,15 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
onShortcutEnabledChanged();
}
- private void updateDisplayLayout(int displayId) {
+ @VisibleForTesting
+ void updateDisplayLayout(int displayId) {
final DisplayLayout newDisplayLayout = mDisplayController.getDisplayLayout(displayId);
+ if (newDisplayLayout == null) {
+ Slog.w(TAG, "Failed to get new DisplayLayout.");
+ return;
+ }
mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
mTutorialHandler.onDisplayChanged(newDisplayLayout);
- mBackgroundPanelOrganizer.onDisplayChanged(newDisplayLayout);
}
private ContentObserver getObserver(Runnable onChangeRunnable) {
@@ -517,11 +511,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
: OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF);
setOneHandedEnabled(enabled);
-
- // Also checks swipe to notification settings since they all need gesture overlay.
- setEnabledGesturalOverlay(
- enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- mContext.getContentResolver(), mUserId), true /* DelayExecute */);
}
@VisibleForTesting
@@ -586,7 +575,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
if (!mIsOneHandedEnabled) {
mDisplayAreaOrganizer.unregisterOrganizer();
- mBackgroundPanelOrganizer.unregisterOrganizer();
// Do NOT register + unRegister DA in the same call
return;
}
@@ -595,45 +583,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mDisplayAreaOrganizer.registerOrganizer(
OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
}
-
- if (!mBackgroundPanelOrganizer.isRegistered()) {
- mBackgroundPanelOrganizer.registerOrganizer(
- OneHandedBackgroundPanelOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL);
- }
- }
-
- private void setupGesturalOverlay() {
- if (!mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- mContext.getContentResolver(), mUserId)) {
- return;
- }
-
- OverlayInfo info = null;
- try {
- mOverlayManager.setHighestPriority(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT);
- info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT);
- } catch (RemoteException e) { /* Do nothing */ }
-
- if (info != null && !info.isEnabled()) {
- // Enable the default gestural one handed overlay.
- setEnabledGesturalOverlay(true /* enabled */, false /* delayExecute */);
- }
- }
-
- @VisibleForTesting
- private void setEnabledGesturalOverlay(boolean enabled, boolean delayExecute) {
- if (mState.isTransitioning() || delayExecute) {
- // Enabled overlay package may affect the current animation(e.g:Settings switch),
- // so we delay 250ms to enabled overlay after switch animation finish, only delay once.
- mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(enabled, false),
- OVERLAY_ENABLED_DELAY_MS);
- return;
- }
- try {
- mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
}
@VisibleForTesting
@@ -648,13 +597,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
}
private void onConfigChanged(Configuration newConfig) {
- if (mTutorialHandler == null || mBackgroundPanelOrganizer == null) {
+ if (mTutorialHandler == null) {
return;
}
if (!mIsOneHandedEnabled || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
return;
}
- mBackgroundPanelOrganizer.onConfigurationChanged();
mTutorialHandler.onConfigurationChanged();
}
@@ -685,10 +633,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
pw.print(innerPrefix + "mIsSwipeToNotificationEnabled=");
pw.println(mIsSwipeToNotificationEnabled);
- if (mBackgroundPanelOrganizer != null) {
- mBackgroundPanelOrganizer.dump(pw);
- }
-
if (mDisplayAreaOrganizer != null) {
mDisplayAreaOrganizer.dump(pw);
}
@@ -714,19 +658,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
}
mOneHandedSettingsUtil.dump(pw, innerPrefix, mContext.getContentResolver(), mUserId);
-
- if (mOverlayManager != null) {
- OverlayInfo info = null;
- try {
- info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY,
- USER_CURRENT);
- } catch (RemoteException e) { /* Do nothing */ }
-
- if (info != null && !info.isEnabled()) {
- pw.print(innerPrefix + "OverlayInfo=");
- pw.println(info);
- }
- }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 1b2f4768110b..f61d1b95bd85 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -16,12 +16,15 @@
package com.android.wm.shell.onehanded;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_ENTER_TRANSITION;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_ONE_HANDED_EXIT_TRANSITION;
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_EXIT;
import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSITION_DIRECTION_TRIGGER;
import android.content.Context;
import android.graphics.Rect;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.view.SurfaceControl;
import android.window.DisplayAreaAppearedInfo;
@@ -34,6 +37,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -41,6 +45,7 @@ import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Manages OneHanded display areas such as offset.
@@ -62,6 +67,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
private final Rect mLastVisualDisplayBounds = new Rect();
private final Rect mDefaultDisplayBounds = new Rect();
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
+ private final InteractionJankMonitor mJankMonitor;
+ private final Context mContext;
private boolean mIsReady;
private float mLastVisualOffset = 0;
@@ -73,7 +80,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
mSurfaceControlTransactionFactory;
private OneHandedTutorialHandler mTutorialHandler;
private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
- private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
@VisibleForTesting
OneHandedAnimationCallback mOneHandedAnimationCallback =
@@ -95,7 +101,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
+ final boolean isEntering = animator.getTransitionDirection()
+ == TRANSITION_DIRECTION_TRIGGER;
if (mAnimationController.isAnimatorsConsumed()) {
+ endCUJTracing(isEntering ? CUJ_ONE_HANDED_ENTER_TRANSITION
+ : CUJ_ONE_HANDED_EXIT_TRANSITION);
finishOffset((int) animator.getDestinationOffset(),
animator.getTransitionDirection());
}
@@ -105,7 +115,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
public void onOneHandedAnimationCancel(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
+ final boolean isEntering = animator.getTransitionDirection()
+ == TRANSITION_DIRECTION_TRIGGER;
if (mAnimationController.isAnimatorsConsumed()) {
+ cancelCUJTracing(isEntering ? CUJ_ONE_HANDED_ENTER_TRANSITION
+ : CUJ_ONE_HANDED_EXIT_TRANSITION);
finishOffset((int) animator.getDestinationOffset(),
animator.getTransitionDirection());
}
@@ -120,20 +134,20 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
OneHandedSettingsUtil oneHandedSettingsUtil,
OneHandedAnimationController animationController,
OneHandedTutorialHandler tutorialHandler,
- OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
+ InteractionJankMonitor jankMonitor,
ShellExecutor mainExecutor) {
super(mainExecutor);
- mDisplayLayout.set(displayLayout);
+ mContext = context;
+ setDisplayLayout(displayLayout);
mOneHandedSettingsUtil = oneHandedSettingsUtil;
- updateDisplayBounds();
mAnimationController = animationController;
+ mJankMonitor = jankMonitor;
final int animationDurationConfig = context.getResources().getInteger(
R.integer.config_one_handed_translate_animation_duration);
mEnterExitAnimationDurationMs =
SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION,
animationDurationConfig);
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
- mBackgroundPanelOrganizer = oneHandedBackgroundGradientOrganizer;
mTutorialHandler = tutorialHandler;
}
@@ -198,6 +212,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
final int direction = yOffset > 0
? TRANSITION_DIRECTION_TRIGGER
: TRANSITION_DIRECTION_EXIT;
+ if (direction == TRANSITION_DIRECTION_TRIGGER) {
+ beginCUJTracing(CUJ_ONE_HANDED_ENTER_TRANSITION, "enterOneHanded");
+ } else {
+ beginCUJTracing(CUJ_ONE_HANDED_EXIT_TRANSITION, "stopOneHanded");
+ }
mDisplayAreaTokenMap.forEach(
(token, leash) -> {
animateWindows(token, leash, fromPos, yOffset, direction,
@@ -236,7 +255,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
animator.setTransitionDirection(direction)
.addOneHandedAnimationCallback(mOneHandedAnimationCallback)
.addOneHandedAnimationCallback(mTutorialHandler)
- .addOneHandedAnimationCallback(mBackgroundPanelOrganizer)
.setDuration(durationMs)
.start();
}
@@ -282,6 +300,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
@VisibleForTesting
void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
mDisplayLayout.set(displayLayout);
+ updateDisplayBounds();
}
@VisibleForTesting
@@ -289,6 +308,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
return mDisplayAreaTokenMap;
}
+ @VisibleForTesting
void updateDisplayBounds() {
mDefaultDisplayBounds.set(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
mLastVisualDisplayBounds.set(mDefaultDisplayBounds);
@@ -301,6 +321,26 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
mTransitionCallbacks.add(callback);
}
+ void beginCUJTracing(@InteractionJankMonitor.CujType int cujType, @Nullable String tag) {
+ final Map.Entry<WindowContainerToken, SurfaceControl> firstEntry =
+ getDisplayAreaTokenMap().entrySet().iterator().next();
+ final InteractionJankMonitor.Configuration.Builder builder =
+ InteractionJankMonitor.Configuration.Builder.withSurface(
+ cujType, mContext, firstEntry.getValue());
+ if (!TextUtils.isEmpty(tag)) {
+ builder.setTag(tag);
+ }
+ mJankMonitor.begin(builder);
+ }
+
+ void endCUJTracing(@InteractionJankMonitor.CujType int cujType) {
+ mJankMonitor.end(cujType);
+ }
+
+ void cancelCUJTracing(@InteractionJankMonitor.CujType int cujType) {
+ mJankMonitor.cancel(cujType);
+ }
+
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index 81dd60d715e9..04e8cf9d2c44 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -64,6 +64,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
private final float mTutorialHeightRatio;
private final WindowManager mWindowManager;
+ private final BackgroundWindowManager mBackgroundWindowManager;
private @OneHandedState.State int mCurrentState;
private int mTutorialAreaHeight;
@@ -78,9 +79,10 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
private int mAlphaAnimationDurationMs;
public OneHandedTutorialHandler(Context context, OneHandedSettingsUtil settingsUtil,
- WindowManager windowManager) {
+ WindowManager windowManager, BackgroundWindowManager backgroundWindowManager) {
mContext = context;
mWindowManager = windowManager;
+ mBackgroundWindowManager = backgroundWindowManager;
mTutorialHeightRatio = settingsUtil.getTranslationFraction(context);
mAlphaAnimationDurationMs = settingsUtil.getTransitionDuration(context);
}
@@ -109,8 +111,19 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
}
@Override
+ public void onStartFinished(Rect bounds) {
+ fillBackgroundColor();
+ }
+
+ @Override
+ public void onStopFinished(Rect bounds) {
+ removeBackgroundSurface();
+ }
+
+ @Override
public void onStateChanged(int newState) {
mCurrentState = newState;
+ mBackgroundWindowManager.onStateChanged(newState);
switch (newState) {
case STATE_ENTERING:
createViewAndAttachToWindow(mContext);
@@ -125,7 +138,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
case STATE_NONE:
checkTransitionEnd();
removeTutorialFromWindowManager();
- break;
default:
break;
}
@@ -145,6 +157,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
}
mTutorialAreaHeight = Math.round(mDisplayBounds.height() * mTutorialHeightRatio);
mAlphaTransitionStart = mTutorialAreaHeight * START_TRANSITION_FRACTION;
+ mBackgroundWindowManager.onDisplayChanged(displayLayout);
}
@VisibleForTesting
@@ -168,6 +181,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
private void attachTargetToWindow() {
try {
mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
+ mBackgroundWindowManager.showBackgroundLayer();
} catch (IllegalStateException e) {
// This shouldn't happen, but if the target is already added, just update its
// layout params.
@@ -185,6 +199,11 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
mTargetViewContainer = null;
}
+ @VisibleForTesting
+ void removeBackgroundSurface() {
+ mBackgroundWindowManager.removeBackgroundLayer();
+ }
+
/**
* Returns layout params for the dismiss target, using the latest display metrics.
*/
@@ -212,9 +231,12 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
* onConfigurationChanged events for updating tutorial text.
*/
public void onConfigurationChanged() {
+ mBackgroundWindowManager.onConfigurationChanged();
+
removeTutorialFromWindowManager();
if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
createViewAndAttachToWindow(mContext);
+ fillBackgroundColor();
updateThemeColor();
checkTransitionEnd();
}
@@ -246,6 +268,14 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
tutorialDesc.setTextColor(themedTextColorSecondary);
}
+ private void fillBackgroundColor() {
+ if (mTargetViewContainer == null || mBackgroundWindowManager == null) {
+ return;
+ }
+ mTargetViewContainer.setBackgroundColor(
+ mBackgroundWindowManager.getThemeColorForBackground());
+ }
+
private void setupAlphaTransition(boolean isEntering) {
final float start = isEntering ? 0.0f : 1.0f;
final float end = isEntering ? 1.0f : 0.0f;
@@ -281,5 +311,9 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
pw.println(mAlphaTransitionStart);
pw.print(innerPrefix + "mAlphaAnimationDurationMs=");
pw.println(mAlphaAnimationDurationMs);
+
+ if (mBackgroundWindowManager != null) {
+ mBackgroundWindowManager.dump(pw);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 9575b0a720bc..e61617281286 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -584,9 +584,11 @@ public class PipAnimationController {
setCurrentValue(bounds);
if (inScaleTransition() || sourceHintRect == null) {
if (isOutPipDirection) {
- getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
+ getSurfaceTransactionHelper().crop(tx, leash, end)
+ .scale(tx, leash, end, bounds);
} else {
- getSurfaceTransactionHelper().scale(tx, leash, base, bounds, angle)
+ getSurfaceTransactionHelper().crop(tx, leash, base)
+ .scale(tx, leash, base, bounds, angle)
.round(tx, leash, base, bounds);
}
} else {
@@ -622,13 +624,13 @@ public class PipAnimationController {
if (rotationDelta == ROTATION_90) {
degree = 90 * (1 - fraction);
x = fraction * (end.left - start.left)
- + start.left + start.right * (1 - fraction);
+ + start.left + start.width() * (1 - fraction);
y = fraction * (end.top - start.top) + start.top;
} else {
degree = -90 * (1 - fraction);
x = fraction * (end.left - start.left) + start.left;
y = fraction * (end.top - start.top)
- + start.top + start.bottom * (1 - fraction);
+ + start.top + start.height() * (1 - fraction);
}
} else {
if (rotationDelta == ROTATION_90) {
@@ -646,8 +648,10 @@ public class PipAnimationController {
getSurfaceTransactionHelper()
.rotateAndScaleWithCrop(tx, leash, initialContainerRect, bounds,
insets, degree, x, y, isOutPipDirection,
- rotationDelta == ROTATION_270 /* clockwise */)
- .round(tx, leash, sourceBounds, bounds);
+ rotationDelta == ROTATION_270 /* clockwise */);
+ if (shouldApplyCornerRadius()) {
+ getSurfaceTransactionHelper().round(tx, leash, sourceBounds, bounds);
+ }
tx.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index a4b866aa3f5e..1a3c51ea4f92 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -44,7 +44,7 @@ public class PipBoundsAlgorithm {
private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
- private final @NonNull PipBoundsState mPipBoundsState;
+ protected final @NonNull PipBoundsState mPipBoundsState;
private final PipSnapAlgorithm mSnapAlgorithm;
private float mDefaultSizePercent;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index 97139626a3d2..8a50f2233573 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -144,7 +144,7 @@ public class PipMediaController {
mediaControlFilter.addAction(ACTION_NEXT);
mediaControlFilter.addAction(ACTION_PREV);
mContext.registerReceiverForAllUsers(mMediaActionReceiver, mediaControlFilter,
- SYSTEMUI_PERMISSION, mainHandler);
+ SYSTEMUI_PERMISSION, mainHandler, Context.RECEIVER_EXPORTED);
// Creates the standard media buttons that we may show.
mPauseAction = getDefaultRemoteAction(R.string.pip_pause,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index a201616db208..6b0d7f5fa461 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -25,6 +25,8 @@ import static android.util.RotationUtils.rotateBounds;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
import static com.android.wm.shell.pip.PipAnimationController.FRACTION_START;
@@ -40,6 +42,10 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isRemovePipDirection;
+import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -391,30 +397,55 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
- mPipUiEventLoggerLogger.log(
- PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ if (ENABLE_SHELL_TRANSITIONS) {
+ if (requestEnterSplit && mSplitScreenOptional.isPresent()) {
+ mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo,
+ isPipTopLeft()
+ ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ mPipTransitionController.startExitTransition(
+ TRANSIT_EXIT_PIP_TO_SPLIT, wct, null /* destinationBounds */);
+ return;
+ }
+ }
+
final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
- final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
- mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds());
- tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
- // We set to fullscreen here for now, but later it will be set to UNDEFINED for
- // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
- wct.setActivityWindowingMode(mToken,
- direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit
- ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- : WINDOWING_MODE_FULLSCREEN);
- wct.setBounds(mToken, destinationBounds);
- wct.setBoundsChangeTransaction(mToken, tx);
+
+ if (Transitions.ENABLE_SHELL_TRANSITIONS && direction == TRANSITION_DIRECTION_LEAVE_PIP) {
+ // When exit to fullscreen with Shell transition enabled, we update the Task windowing
+ // mode directly so that it can also trigger display rotation and visibility update in
+ // the same transition if there will be any.
+ wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ // We can inherit the parent bounds as it is going to be fullscreen. The
+ // destinationBounds calculated above will be incorrect if this is with rotation.
+ wct.setBounds(mToken, null);
+ } else {
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
+ mPipBoundsState.getBounds());
+ tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
+ // We set to fullscreen here for now, but later it will be set to UNDEFINED for
+ // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
+ wct.setActivityWindowingMode(mToken,
+ direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+ && !requestEnterSplit
+ ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ : WINDOWING_MODE_FULLSCREEN);
+ wct.setBounds(mToken, destinationBounds);
+ wct.setBoundsChangeTransaction(mToken, tx);
+ }
+
// Set the exiting state first so if there is fixed rotation later, the running animation
// won't be interrupted by alpha animation for existing PiP.
mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mPipTransitionController.startTransition(destinationBounds, wct);
+ mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds);
return;
}
mSyncTransactionQueue.queue(wct);
@@ -479,7 +510,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
wct.setBounds(mToken, null);
wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
wct.reorder(mToken, false);
- mPipTransitionController.startTransition(null, wct);
+ mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct,
+ null /* destinationBounds */);
return;
}
@@ -710,24 +742,18 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
return;
}
+ if (Transitions.ENABLE_SHELL_TRANSITIONS
+ && mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP) {
+ // With Shell transition, we do the cleanup in PipTransition after exiting PIP.
+ return;
+ }
final WindowContainerToken token = info.token;
Objects.requireNonNull(token, "Requires valid WindowContainerToken");
if (token.asBinder() != mToken.asBinder()) {
Log.wtf(TAG, "Unrecognized token: " + token);
return;
}
- clearWaitForFixedRotation();
- mPipTransitionState.setInSwipePipToHomeTransition(false);
- mPictureInPictureParams = null;
- mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
- // Re-set the PIP bounds to none.
- mPipBoundsState.setBounds(new Rect());
- mPipUiEventLoggerLogger.setTaskInfo(null);
- mPipMenuController.detach();
-
- if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
- mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
- }
+ onExitPipFinished(info);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mPipTransitionController.forceFinishTransition();
@@ -824,6 +850,22 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
clearWaitForFixedRotation();
}
+ /** Called when exiting PIP tranisiton is finished to do the state cleanup. */
+ void onExitPipFinished(TaskInfo info) {
+ clearWaitForFixedRotation();
+ mPipTransitionState.setInSwipePipToHomeTransition(false);
+ mPictureInPictureParams = null;
+ mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
+ // Re-set the PIP bounds to none.
+ mPipBoundsState.setBounds(new Rect());
+ mPipUiEventLoggerLogger.setTaskInfo(null);
+ mPipMenuController.detach();
+
+ if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
+ mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
+ }
+ }
+
private void fadeExistingPip(boolean show) {
if (mLeash == null || !mLeash.isValid()) {
Log.w(TAG, "Invalid leash on fadeExistingPip: " + mLeash);
@@ -886,7 +928,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (animator == null || !animator.isRunning()
|| animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
final boolean rotatingPip = mPipTransitionState.isInPip() && fromRotation;
- if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) {
+ if (rotatingPip && Transitions.ENABLE_SHELL_TRANSITIONS) {
+ // The animation and surface update will be handled by the shell transition handler.
+ mPipBoundsState.setBounds(destinationBoundsOut);
+ } else if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) {
// The position will be used by fade-in animation when the fixed rotation is done.
mPipBoundsState.setBounds(destinationBoundsOut);
} else if (rotatingPip) {
@@ -1281,7 +1326,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
@PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) {
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
- mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct);
+ mSplitScreenOptional.ifPresent(splitScreenController ->
+ splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
} else {
mTaskOrganizer.applyTransaction(wct);
}
@@ -1416,7 +1462,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
/**
* Fades out and removes an overlay surface.
*/
- private void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback,
+ void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback,
boolean withStartDelay) {
if (surface == null) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index b31e6e0750ce..3e5d5f645725 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -19,8 +19,14 @@ package com.android.wm.shell.pip;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.util.RotationUtils.deltaRotation;
+import static android.util.RotationUtils.rotateBounds;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PIP;
+import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
@@ -30,19 +36,22 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
+import static com.android.wm.shell.transition.Transitions.isOpeningType;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
-import android.util.Log;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -50,8 +59,12 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.CounterRotatorHelper;
import com.android.wm.shell.transition.Transitions;
+import java.util.Optional;
+
/**
* Implementation of transitions for PiP on phone. Responsible for enter (alpha, bounds) and
* exit animation.
@@ -60,12 +73,19 @@ public class PipTransition extends PipTransitionController {
private static final String TAG = PipTransition.class.getSimpleName();
+ private final Context mContext;
private final PipTransitionState mPipTransitionState;
private final int mEnterExitAnimationDuration;
+ private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+ private final Optional<SplitScreenController> mSplitScreenOptional;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
- private Rect mExitDestinationBounds = new Rect();
- private IBinder mExitTransition = null;
+ private final Rect mExitDestinationBounds = new Rect();
+ @Nullable
+ private IBinder mExitTransition;
+ /** The Task window that is currently in PIP windowing mode. */
+ @Nullable
+ private WindowContainerToken mCurrentPipTaskToken;
public PipTransition(Context context,
PipBoundsState pipBoundsState,
@@ -74,12 +94,17 @@ public class PipTransition extends PipTransitionController {
PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController,
Transitions transitions,
- @NonNull ShellTaskOrganizer shellTaskOrganizer) {
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ Optional<SplitScreenController> splitScreenOptional) {
super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
pipAnimationController, transitions, shellTaskOrganizer);
+ mContext = context;
mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
+ mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
+ mSplitScreenOptional = splitScreenOptional;
}
@Override
@@ -97,97 +122,78 @@ public class PipTransition extends PipTransitionController {
}
@Override
- public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ public void startExitTransition(int type, WindowContainerTransaction out,
+ @Nullable Rect destinationBounds) {
if (destinationBounds != null) {
mExitDestinationBounds.set(destinationBounds);
- mExitTransition = mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
- } else {
- mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this);
}
+ mExitTransition = mTransitions.startTransition(type, out, this);
}
@Override
- public boolean startAnimation(@android.annotation.NonNull IBinder transition,
- @android.annotation.NonNull TransitionInfo info,
- @android.annotation.NonNull SurfaceControl.Transaction startTransaction,
- @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
- @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
-
- if (mExitTransition == transition || info.getType() == TRANSIT_EXIT_PIP) {
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ // Exiting PIP.
+ final int type = info.getType();
+ if (transition.equals(mExitTransition)) {
+ mExitDestinationBounds.setEmpty();
mExitTransition = null;
- if (info.getChanges().size() == 1) {
- if (mFinishCallback != null) {
- mFinishCallback.onTransitionFinished(null, null);
- mFinishCallback = null;
- throw new RuntimeException("Previous callback not called, aborting exit PIP.");
- }
-
- final TransitionInfo.Change change = info.getChanges().get(0);
- mFinishCallback = finishCallback;
- startTransaction.apply();
- boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(),
- new Rect(mExitDestinationBounds));
- mExitDestinationBounds.setEmpty();
- return success;
- } else {
- Log.e(TAG, "Got an exit-pip transition with unexpected change-list");
- }
- }
- if (info.getType() == TRANSIT_REMOVE_PIP) {
if (mFinishCallback != null) {
- mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
+ mFinishCallback.onTransitionFinished(null, null);
mFinishCallback = null;
- throw new RuntimeException("Previous callback not called, aborting remove PIP.");
+ throw new RuntimeException("Previous callback not called, aborting exit PIP.");
}
- startTransaction.apply();
- finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
- mPipBoundsState.getDisplayBounds());
- finishCallback.onTransitionFinished(null, null);
- return true;
- }
-
- // We only support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
- // that enter PiP instantly on opening, mostly from CTS/Flicker tests)
- if (info.getType() != TRANSIT_PIP && info.getType() != TRANSIT_OPEN) {
- return false;
- }
+ final TransitionInfo.Change exitPipChange = findCurrentPipChange(info);
+ if (exitPipChange == null) {
+ throw new RuntimeException("Cannot find the pip window for exit-pip transition.");
+ }
- // Search for an Enter PiP transition (along with a show wallpaper one)
- TransitionInfo.Change enterPip = null;
- TransitionInfo.Change wallpaper = null;
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getTaskInfo() != null
- && change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_PINNED) {
- enterPip = change;
- } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
- wallpaper = change;
+ switch (type) {
+ case TRANSIT_EXIT_PIP:
+ startExitAnimation(info, startTransaction, finishTransaction, finishCallback,
+ exitPipChange);
+ break;
+ case TRANSIT_EXIT_PIP_TO_SPLIT:
+ startExitToSplitAnimation(info, startTransaction, finishTransaction,
+ finishCallback, exitPipChange);
+ break;
+ case TRANSIT_REMOVE_PIP:
+ removePipImmediately(info, startTransaction, finishTransaction, finishCallback,
+ exitPipChange);
+ break;
+ default:
+ throw new IllegalStateException("mExitTransition with unexpected transit type="
+ + transitTypeToString(type));
}
- }
- if (enterPip == null) {
- return false;
+ mCurrentPipTaskToken = null;
+ return true;
}
- if (mFinishCallback != null) {
- mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
- mFinishCallback = null;
- throw new RuntimeException("Previous callback not called, aborting entering PIP.");
+ // The previous PIP Task is no longer in PIP, but this is not an exit transition (This can
+ // happen when a new activity requests enter PIP). In this case, we just show this Task in
+ // its end state, and play other animation as normal.
+ final TransitionInfo.Change currentPipChange = findCurrentPipChange(info);
+ if (currentPipChange != null
+ && currentPipChange.getTaskInfo().getWindowingMode() != WINDOWING_MODE_PINNED) {
+ resetPrevPip(currentPipChange, startTransaction);
}
- // Show the wallpaper if there is a wallpaper change.
- if (wallpaper != null) {
- startTransaction.show(wallpaper.getLeash());
- startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
+ // Entering PIP.
+ if (isEnteringPip(info, mCurrentPipTaskToken)) {
+ return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
}
- mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
- mFinishCallback = finishCallback;
- return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
- startTransaction, finishTransaction, enterPip.getStartRotation(),
- enterPip.getEndRotation());
+ // For transition that we don't animate, but contains the PIP leash, we need to update the
+ // PIP surface, otherwise it will be reset after the transition.
+ if (currentPipChange != null) {
+ updatePipForUnhandledTransition(currentPipChange, startTransaction, finishTransaction);
+ }
+ return false;
}
@Nullable
@@ -196,7 +202,6 @@ public class PipTransition extends PipTransitionController {
@NonNull TransitionRequestInfo request) {
if (request.getType() == TRANSIT_PIP) {
WindowContainerTransaction wct = new WindowContainerTransaction();
- mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
wct.setActivityWindowingMode(request.getTriggerTask().token,
WINDOWING_MODE_UNDEFINED);
@@ -230,6 +235,7 @@ public class PipTransition extends PipTransitionController {
new Rect(mExitDestinationBounds));
}
mExitDestinationBounds.setEmpty();
+ mCurrentPipTaskToken = null;
}
@Override
@@ -262,7 +268,118 @@ public class PipTransition extends PipTransitionController {
mFinishCallback = null;
}
- private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
+ @Nullable
+ private TransitionInfo.Change findCurrentPipChange(@NonNull TransitionInfo info) {
+ if (mCurrentPipTaskToken == null) {
+ return null;
+ }
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (mCurrentPipTaskToken.equals(change.getContainer())) {
+ return change;
+ }
+ }
+ return null;
+ }
+
+ private void startExitAnimation(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull TransitionInfo.Change pipChange) {
+ TransitionInfo.Change displayRotationChange = null;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getMode() == TRANSIT_CHANGE
+ && (change.getFlags() & FLAG_IS_DISPLAY) != 0
+ && change.getStartRotation() != change.getEndRotation()) {
+ displayRotationChange = change;
+ break;
+ }
+ }
+
+ if (displayRotationChange != null) {
+ // Exiting PIP to fullscreen with orientation change.
+ startExpandAndRotationAnimation(info, startTransaction, finishTransaction,
+ finishCallback, displayRotationChange, pipChange);
+ return;
+ }
+
+ // When there is no rotation, we can simply expand the PIP window.
+ mFinishCallback = (wct, wctCB) -> {
+ mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+ finishCallback.onTransitionFinished(wct, wctCB);
+ };
+
+ // Set the initial frame as scaling the end to the start.
+ final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds());
+ final Point offset = pipChange.getEndRelOffset();
+ destinationBounds.offset(-offset.x, -offset.y);
+ startTransaction.setWindowCrop(pipChange.getLeash(), destinationBounds);
+ mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(),
+ destinationBounds, mPipBoundsState.getBounds());
+ startTransaction.apply();
+ startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds);
+ }
+
+ private void startExpandAndRotationAnimation(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull TransitionInfo.Change displayRotationChange,
+ @NonNull TransitionInfo.Change pipChange) {
+ final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(),
+ displayRotationChange.getEndRotation());
+
+ // Counter-rotate all "going-away" things since they are still in the old orientation.
+ final CounterRotatorHelper rotator = new CounterRotatorHelper();
+ rotator.handleClosingChanges(info, startTransaction, displayRotationChange);
+
+ mFinishCallback = (wct, wctCB) -> {
+ mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+ finishCallback.onTransitionFinished(wct, wctCB);
+ };
+
+ // Get the start bounds in new orientation.
+ final Rect startBounds = new Rect(pipChange.getStartAbsBounds());
+ rotateBounds(startBounds, displayRotationChange.getStartAbsBounds(), rotateDelta);
+ final Rect endBounds = new Rect(pipChange.getEndAbsBounds());
+ final Point offset = pipChange.getEndRelOffset();
+ startBounds.offset(-offset.x, -offset.y);
+ endBounds.offset(-offset.x, -offset.y);
+
+ // Reverse the rotation direction for expansion.
+ final int pipRotateDelta = deltaRotation(rotateDelta, 0);
+
+ // Set the start frame.
+ final int degree, x, y;
+ if (pipRotateDelta == ROTATION_90) {
+ degree = 90;
+ x = startBounds.right;
+ y = startBounds.top;
+ } else {
+ degree = -90;
+ x = startBounds.left;
+ y = startBounds.bottom;
+ }
+ mSurfaceTransactionHelper.rotateAndScaleWithCrop(startTransaction, pipChange.getLeash(),
+ endBounds, startBounds, new Rect(), degree, x, y, true /* isExpanding */,
+ pipRotateDelta == ROTATION_270 /* clockwise */);
+ startTransaction.apply();
+ rotator.cleanUp(finishTransaction);
+
+ // Expand and rotate the pip window to fullscreen.
+ final PipAnimationController.PipTransitionAnimator animator =
+ mPipAnimationController.getAnimator(pipChange.getTaskInfo(), pipChange.getLeash(),
+ startBounds, startBounds, endBounds, null, TRANSITION_DIRECTION_LEAVE_PIP,
+ 0 /* startingAngle */, pipRotateDelta);
+ animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(mEnterExitAnimationDuration)
+ .start();
+ }
+
+ private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
final Rect destinationBounds) {
PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(),
@@ -273,8 +390,87 @@ public class PipTransition extends PipTransitionController {
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
.start();
+ }
- return true;
+ /** For {@link Transitions#TRANSIT_REMOVE_PIP}, we just immediately remove the PIP Task. */
+ private void removePipImmediately(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull TransitionInfo.Change pipChange) {
+ startTransaction.apply();
+ finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
+ mPipBoundsState.getDisplayBounds());
+ mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+ finishCallback.onTransitionFinished(null, null);
+ }
+
+ /** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */
+ private static boolean isEnteringPip(@NonNull TransitionInfo info,
+ @Nullable WindowContainerToken currentPipTaskToken) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED
+ && !change.getContainer().equals(currentPipTaskToken)) {
+ // We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
+ // that enter PiP instantly on opening, mostly from CTS/Flicker tests)
+ if (info.getType() == TRANSIT_PIP || info.getType() == TRANSIT_OPEN) {
+ return true;
+ }
+ // This can happen if the request to enter PIP happens when we are collecting for
+ // another transition, such as TRANSIT_CHANGE (display rotation).
+ if (info.getType() == TRANSIT_CHANGE) {
+ return true;
+ }
+
+ // Please file a bug to handle the unexpected transition type.
+ throw new IllegalStateException("Entering PIP with unexpected transition type="
+ + transitTypeToString(info.getType()));
+ }
+ }
+ return false;
+ }
+
+ private boolean startEnterAnimation(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ // Search for an Enter PiP transition (along with a show wallpaper one)
+ TransitionInfo.Change enterPip = null;
+ TransitionInfo.Change wallpaper = null;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
+ enterPip = change;
+ } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+ wallpaper = change;
+ }
+ }
+ if (enterPip == null) {
+ return false;
+ }
+ // Keep track of the PIP task.
+ mCurrentPipTaskToken = enterPip.getContainer();
+
+ if (mFinishCallback != null) {
+ mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
+ mFinishCallback = null;
+ throw new RuntimeException("Previous callback not called, aborting entering PIP.");
+ }
+
+ // Show the wallpaper if there is a wallpaper change.
+ if (wallpaper != null) {
+ startTransaction.show(wallpaper.getLeash());
+ startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
+ }
+
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
+ mFinishCallback = finishCallback;
+ return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
+ startTransaction, finishTransaction, enterPip.getStartRotation(),
+ enterPip.getEndRotation());
}
private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
@@ -286,7 +482,10 @@ public class PipTransition extends PipTransitionController {
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
PipAnimationController.PipTransitionAnimator animator;
- finishTransaction.setPosition(leash, destinationBounds.left, destinationBounds.top);
+ // Set corner radius for entering pip.
+ mSurfaceTransactionHelper
+ .crop(finishTransaction, leash, destinationBounds)
+ .round(finishTransaction, leash, true /* applyCornerRadius */);
if (taskInfo.pictureInPictureParams != null
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()
&& mPipTransitionState.getInSwipePipToHomeTransition()) {
@@ -322,6 +521,11 @@ public class PipTransition extends PipTransitionController {
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
0 /* startingAngle */, rotationDelta);
+ if (sourceHintRect == null) {
+ // We use content overlay when there is no source rect hint to enter PiP use bounds
+ // animation.
+ animator.setUseContentOverlay(mContext);
+ }
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
startTransaction.setAlpha(leash, 0f);
// PiP menu is attached late in the process here to avoid any artifacts on the leash
@@ -343,6 +547,74 @@ public class PipTransition extends PipTransitionController {
return true;
}
+ private void startExitToSplitAnimation(TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
+ Transitions.TransitionFinishCallback finishCallback,
+ TransitionInfo.Change pipChange) {
+ final int changeSize = info.getChanges().size();
+ if (changeSize < 4) {
+ throw new RuntimeException(
+ "Got an exit-pip-to-split transition with unexpected change-list");
+ }
+ for (int i = changeSize - 1; i >= 0; i--) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final int mode = change.getMode();
+
+ if (mode == TRANSIT_CHANGE && change.getParent() != null) {
+ // TODO: perform resize/expand animation for reparented child task.
+ continue;
+ }
+
+ if (isOpeningType(mode) && change.getParent() == null) {
+ final SurfaceControl leash = change.getLeash();
+ final Rect endBounds = change.getEndAbsBounds();
+ startTransaction
+ .show(leash)
+ .setAlpha(leash, 1f)
+ .setPosition(leash, endBounds.left, endBounds.top)
+ .setWindowCrop(leash, endBounds.width(), endBounds.height());
+ }
+ }
+ mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction);
+ startTransaction.apply();
+
+ mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
+ finishCallback.onTransitionFinished(null, null);
+ }
+
+ private void resetPrevPip(@NonNull TransitionInfo.Change prevPipChange,
+ @NonNull SurfaceControl.Transaction startTransaction) {
+ final SurfaceControl leash = prevPipChange.getLeash();
+ final Rect bounds = prevPipChange.getEndAbsBounds();
+ final Point offset = prevPipChange.getEndRelOffset();
+ bounds.offset(-offset.x, -offset.y);
+
+ startTransaction.setWindowCrop(leash, null);
+ startTransaction.setMatrix(leash, 1, 0, 0, 1);
+ startTransaction.setCornerRadius(leash, 0);
+ startTransaction.setPosition(leash, bounds.left, bounds.top);
+
+ mCurrentPipTaskToken = null;
+ mPipOrganizer.onExitPipFinished(prevPipChange.getTaskInfo());
+ }
+
+ private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ // When the PIP window is visible and being a part of the transition, such as display
+ // rotation, we need to update its bounds and rounded corner.
+ final SurfaceControl leash = pipChange.getLeash();
+ final Rect destBounds = mPipBoundsState.getBounds();
+ final boolean isInPip = mPipTransitionState.isInPip();
+ mSurfaceTransactionHelper
+ .crop(startTransaction, leash, destBounds)
+ .round(startTransaction, leash, isInPip);
+ mSurfaceTransactionHelper
+ .crop(finishTransaction, leash, destBounds)
+ .round(finishTransaction, leash, isInPip);
+ }
+
private void finishResizeForMenu(Rect destinationBounds) {
mPipMenuController.movePipMenu(null, null, destinationBounds);
mPipMenuController.updateMenuBounds(destinationBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 376f3298a83c..22b3ef3bfe0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -19,7 +19,9 @@ package com.android.wm.shell.pip;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
+import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
+import android.annotation.Nullable;
import android.app.PictureInPictureParams;
import android.app.TaskInfo;
import android.content.ComponentName;
@@ -68,6 +70,10 @@ public abstract class PipTransitionController implements Transitions.TransitionH
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
return;
}
+ if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ animator::clearContentOverlay, true /* withStartDelay*/);
+ }
onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx);
sendOnPipTransitionFinished(direction);
}
@@ -75,6 +81,11 @@ public abstract class PipTransitionController implements Transitions.TransitionH
@Override
public void onPipAnimationCancel(TaskInfo taskInfo,
PipAnimationController.PipTransitionAnimator animator) {
+ final int direction = animator.getTransitionDirection();
+ if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ animator::clearContentOverlay, true /* withStartDelay */);
+ }
sendOnPipTransitionCancelled(animator.getTransitionDirection());
}
};
@@ -98,9 +109,10 @@ public abstract class PipTransitionController implements Transitions.TransitionH
}
/**
- * Called when the Shell wants to starts a transition/animation.
+ * Called when the Shell wants to start an exit Pip transition/animation.
*/
- public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ public void startExitTransition(int type, WindowContainerTransaction out,
+ @Nullable Rect destinationBounds) {
// Default implementation does nothing.
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
index a0a76d801cf4..9c23a32a7d2b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
@@ -107,7 +107,10 @@ public class PipUiEventLogger {
PICTURE_IN_PICTURE_STASH_LEFT(710),
@UiEvent(doc = "User stashed picture-in-picture to the right side")
- PICTURE_IN_PICTURE_STASH_RIGHT(711);
+ PICTURE_IN_PICTURE_STASH_RIGHT(711),
+
+ @UiEvent(doc = "User taps on the settings button in PiP menu")
+ PICTURE_IN_PICTURE_SHOW_SETTINGS(933);
private final int mId;
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 101a55d8d367..6ec8f5b924f8 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
@@ -44,6 +44,7 @@ import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMediaController.ActionListener;
import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
@@ -118,6 +119,7 @@ public class PhonePipMenuController implements PipMenuController {
private final ArrayList<Listener> mListeners = new ArrayList<>();
private final SystemWindows mSystemWindows;
private final Optional<SplitScreenController> mSplitScreenController;
+ private final PipUiEventLogger mPipUiEventLogger;
private ParceledListSlice<RemoteAction> mAppActions;
private ParceledListSlice<RemoteAction> mMediaActions;
private SyncRtSurfaceTransactionApplier mApplier;
@@ -150,6 +152,7 @@ public class PhonePipMenuController implements PipMenuController {
public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
PipMediaController mediaController, SystemWindows systemWindows,
Optional<SplitScreenController> splitScreenOptional,
+ PipUiEventLogger pipUiEventLogger,
ShellExecutor mainExecutor, Handler mainHandler) {
mContext = context;
mPipBoundsState = pipBoundsState;
@@ -158,6 +161,7 @@ public class PhonePipMenuController implements PipMenuController {
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
mSplitScreenController = splitScreenOptional;
+ mPipUiEventLogger = pipUiEventLogger;
}
public boolean isMenuVisible() {
@@ -187,7 +191,7 @@ public class PhonePipMenuController implements PipMenuController {
detachPipMenuView();
}
mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler,
- mSplitScreenController);
+ mSplitScreenController, mPipUiEventLogger);
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index a41fd8429e35..d3dc91515921 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -309,7 +309,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
ShellExecutor mainExecutor
) {
// Ensure that we are the primary user's SystemUI.
- final int processUser = UserManager.get(context).getUserHandle();
+ final int processUser = UserManager.get(context).getProcessUserId();
if (processUser != UserHandle.USER_SYSTEM) {
throw new IllegalStateException("Non-primary Pip component not currently supported.");
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index e1475efcdb57..225305bd5178 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -63,6 +63,7 @@ import android.widget.LinearLayout;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -119,8 +120,9 @@ public class PipMenuView extends FrameLayout {
private int mBetweenActionPaddingLand;
private AnimatorSet mMenuContainerAnimator;
- private PhonePipMenuController mController;
- private Optional<SplitScreenController> mSplitScreenControllerOptional;
+ private final PhonePipMenuController mController;
+ private final Optional<SplitScreenController> mSplitScreenControllerOptional;
+ private final PipUiEventLogger mPipUiEventLogger;
private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -150,13 +152,15 @@ public class PipMenuView extends FrameLayout {
public PipMenuView(Context context, PhonePipMenuController controller,
ShellExecutor mainExecutor, Handler mainHandler,
- Optional<SplitScreenController> splitScreenController) {
+ Optional<SplitScreenController> splitScreenController,
+ PipUiEventLogger pipUiEventLogger) {
super(context, null, 0);
mContext = context;
mController = controller;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
mSplitScreenControllerOptional = splitScreenController;
+ mPipUiEventLogger = pipUiEventLogger;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
inflate(context, R.layout.pip_menu, this);
@@ -539,6 +543,8 @@ public class PipMenuView extends FrameLayout {
// handles the message
hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* resize */,
ANIM_TYPE_HIDE);
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
}
private void dismissPip() {
@@ -547,6 +553,7 @@ public class PipMenuView extends FrameLayout {
// any other dismissal that will update the touch state and fade out the PIP task
// and the menu view at the same time.
mController.onPipDismiss();
+ mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE);
}
}
@@ -566,6 +573,7 @@ public class PipMenuView extends FrameLayout {
Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
mContext.startActivityAsUser(settingsIntent, UserHandle.of(topPipActivityInfo.second));
+ mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_SHOW_SETTINGS);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 3ace5f405d36..350f2856e2bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -149,7 +149,6 @@ public class PipTouchHandler {
@Override
public void onPipDismiss() {
- mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE);
mTouchState.removeDoubleTapTimeoutCallback();
mMotionHelper.dismissPip();
}
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
new file mode 100644
index 000000000000..33f3bfb7b266
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -0,0 +1,69 @@
+/*
+ * 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.pip.tv;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.Gravity;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipSnapAlgorithm;
+
+/**
+ * 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 = false;
+
+ public TvPipBoundsAlgorithm(Context context,
+ @NonNull PipBoundsState pipBoundsState,
+ @NonNull PipSnapAlgorithm pipSnapAlgorithm) {
+ super(context, pipBoundsState, pipSnapAlgorithm);
+ }
+
+ /**
+ * The normal bounds at a different position on the screen.
+ */
+ public Rect getTvNormalBounds(int gravity) {
+ Rect normalBounds = getNormalBounds();
+ Rect insetBounds = new Rect();
+ getInsetBounds(insetBounds);
+
+ if (mPipBoundsState.isImeShowing()) {
+ if (DEBUG) Log.d(TAG, "IME showing, height: " + mPipBoundsState.getImeHeight());
+ insetBounds.bottom -= mPipBoundsState.getImeHeight();
+ }
+
+ Rect result = new Rect();
+ Gravity.apply(gravity, normalBounds.width(), normalBounds.height(), insetBounds, result);
+
+ if (DEBUG) {
+ Log.d(TAG, "normalBounds: " + normalBounds.toShortString());
+ Log.d(TAG, "insetBounds: " + insetBounds.toShortString());
+ Log.d(TAG, "gravity: " + Gravity.toString(gravity));
+ Log.d(TAG, "resultBounds: " + result.toShortString());
+ }
+
+ return result;
+ }
+}
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 00083d986dbe..de53939c01db 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,6 +18,10 @@ 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_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 android.annotation.IntDef;
import android.app.ActivityManager;
@@ -33,6 +37,7 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
import android.view.DisplayInfo;
+import android.view.Gravity;
import com.android.wm.shell.R;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -42,7 +47,6 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -57,7 +61,7 @@ import java.lang.annotation.RetentionPolicy;
public class TvPipController implements PipTransitionController.PipTransitionCallback,
TvPipMenuController.Delegate, TvPipNotificationController.Delegate {
private static final String TAG = "TvPipController";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
private static final int NONEXISTENT_TASK_ID = -1;
@@ -85,10 +89,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
*/
private static final int STATE_PIP_MENU = 2;
+ private static final int DEFAULT_GRAVITY = Gravity.BOTTOM | Gravity.RIGHT;
+
private final Context mContext;
private final PipBoundsState mPipBoundsState;
- private final PipBoundsAlgorithm mPipBoundsAlgorithm;
+ private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
private final PipTaskOrganizer mPipTaskOrganizer;
private final PipMediaController mPipMediaController;
private final TvPipNotificationController mPipNotificationController;
@@ -97,6 +103,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private final TvPipImpl mImpl = new TvPipImpl();
private @State int mState = STATE_NO_PIP;
+ private @Gravity.GravityFlags int mGravity = DEFAULT_GRAVITY;
private int mPinnedTaskId = NONEXISTENT_TASK_ID;
private int mResizeAnimationDuration;
@@ -104,7 +111,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
public static Pip create(
Context context,
PipBoundsState pipBoundsState,
- PipBoundsAlgorithm pipBoundsAlgorithm,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
PipTransitionController pipTransitionController,
TvPipMenuController tvPipMenuController,
@@ -116,7 +123,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
return new TvPipController(
context,
pipBoundsState,
- pipBoundsAlgorithm,
+ tvPipBoundsAlgorithm,
pipTaskOrganizer,
pipTransitionController,
tvPipMenuController,
@@ -130,7 +137,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private TvPipController(
Context context,
PipBoundsState pipBoundsState,
- PipBoundsAlgorithm pipBoundsAlgorithm,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
PipTransitionController pipTransitionController,
TvPipMenuController tvPipMenuController,
@@ -145,7 +152,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mPipBoundsState = pipBoundsState;
mPipBoundsState.setDisplayId(context.getDisplayId());
mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
- mPipBoundsAlgorithm = pipBoundsAlgorithm;
+ mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
mPipMediaController = pipMediaController;
@@ -192,24 +199,19 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
public void showPictureInPictureMenu() {
if (DEBUG) Log.d(TAG, "showPictureInPictureMenu(), state=" + stateToName(mState));
- if (mState != STATE_PIP) {
+ if (mState == STATE_NO_PIP) {
if (DEBUG) Log.d(TAG, " > cannot open Menu from the current state.");
return;
}
setState(STATE_PIP_MENU);
- resizePinnedStack(STATE_PIP_MENU);
+ movePinnedStack();
}
- /**
- * Moves Pip window to its "normal" position.
- */
@Override
- public void movePipToNormalPosition() {
- if (DEBUG) Log.d(TAG, "movePipToNormalPosition(), state=" + stateToName(mState));
-
+ public void closeMenu() {
+ if (DEBUG) Log.d(TAG, "closeMenu(), state before=" + stateToName(mState));
setState(STATE_PIP);
- resizePinnedStack(STATE_PIP);
}
/**
@@ -223,53 +225,78 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
onPipDisappeared();
}
- /**
- * Closes Pip window.
- */
@Override
- public void closePip() {
- if (DEBUG) Log.d(TAG, "closePip(), state=" + stateToName(mState));
+ public void movePip(int keycode) {
+ if (updatePosition(keycode)) {
+ if (DEBUG) Log.d(TAG, "New gravity: " + Gravity.toString(mGravity));
+ mTvPipMenuController.updateMenu(mGravity);
+ movePinnedStack();
+ } else {
+ if (DEBUG) Log.d(TAG, "Position hasn't changed");
+ }
+ }
- removeTask(mPinnedTaskId);
- onPipDisappeared();
+ @Override
+ public int getPipGravity() {
+ return mGravity;
}
/**
- * Resizes the Pip task/window to the appropriate size for the given state.
- * This is a legacy API. Now we expect that the state argument passed to it should always match
- * the current state of the Controller. If it does not match an {@link IllegalArgumentException}
- * will be thrown. However, if the passed state does match - we'll determine the right bounds
- * to the state and will move Pip task/window there.
- *
- * @param state the to determine the Pip bounds. IMPORTANT: should always match the current
- * state of the Controller.
+ * @return true if position changed
*/
- private void resizePinnedStack(@State int state) {
- if (state != mState) {
- throw new IllegalArgumentException("The passed state should match the current state!");
- }
- if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + stateToName(mState));
+ private boolean updatePosition(int keycode) {
+ if (DEBUG) Log.d(TAG, "updatePosition, keycode: " + keycode);
- final Rect newBounds;
- switch (mState) {
- case STATE_PIP_MENU:
- newBounds = mPipBoundsState.getExpandedBounds();
+ int updatedGravity;
+ switch (keycode) {
+ case KEYCODE_DPAD_UP:
+ updatedGravity = (mGravity & (~Gravity.BOTTOM)) | Gravity.TOP;
break;
-
- case STATE_PIP:
- // Let PipBoundsAlgorithm figure out what the correct bounds are at the moment.
- // Internally, it will get the "default" bounds from PipBoundsState and adjust them
- // as needed to account for things like IME state (will query PipBoundsState for
- // this information as well, so it's important to keep PipBoundsState up to date).
- newBounds = mPipBoundsAlgorithm.getNormalBounds();
+ case KEYCODE_DPAD_DOWN:
+ updatedGravity = (mGravity & (~Gravity.TOP)) | Gravity.BOTTOM;
+ break;
+ case KEYCODE_DPAD_LEFT:
+ updatedGravity = (mGravity & (~Gravity.RIGHT)) | Gravity.LEFT;
+ break;
+ case KEYCODE_DPAD_RIGHT:
+ updatedGravity = (mGravity & (~Gravity.LEFT)) | Gravity.RIGHT;
break;
-
- case STATE_NO_PIP:
default:
- return;
+ updatedGravity = mGravity;
}
- mPipTaskOrganizer.scheduleAnimateResizePip(newBounds, mResizeAnimationDuration, null);
+ if (updatedGravity != mGravity) {
+ mGravity = updatedGravity;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Animate to the updated position of the PiP based on the state and position of the PiP.
+ */
+ private void movePinnedStack() {
+ if (mState == STATE_NO_PIP) {
+ return;
+ }
+
+ Rect bounds = mTvPipBoundsAlgorithm.getTvNormalBounds(mGravity);
+ if (DEBUG) Log.d(TAG, "movePinnedStack() - new pip bounds: " + bounds.toShortString());
+ mPipTaskOrganizer.scheduleAnimateResizePip(bounds,
+ mResizeAnimationDuration, rect -> {
+ if (DEBUG) Log.d(TAG, "movePinnedStack() animation done");
+ });
+ }
+
+ /**
+ * Closes Pip window.
+ */
+ @Override
+ public void closePip() {
+ if (DEBUG) Log.d(TAG, "closePip(), state=" + stateToName(mState));
+
+ removeTask(mPinnedTaskId);
+ onPipDisappeared();
}
private void registerSessionListenerForCurrentUser() {
@@ -301,6 +328,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mPipNotificationController.dismiss();
mTvPipMenuController.hideMenu();
+ mGravity = DEFAULT_GRAVITY;
setState(STATE_NO_PIP);
mPinnedTaskId = NONEXISTENT_TASK_ID;
}
@@ -336,11 +364,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private void loadConfigurations() {
final Resources res = mContext.getResources();
mResizeAnimationDuration = res.getInteger(R.integer.config_pipResizeAnimationDuration);
- // "Cache" bounds for the Pip menu as "expanded" bounds in PipBoundsState. We'll refer back
- // to this value in resizePinnedStack(), when we are adjusting Pip task/window position for
- // the menu.
- mPipBoundsState.setExpandedBounds(
- Rect.unflattenFromString(res.getString(R.string.pip_menu_bounds)));
}
private DisplayInfo getDisplayInfo() {
@@ -392,10 +415,9 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
return;
}
mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
- // "Normal" Pip bounds may have changed, so if we are in the "normal" state,
- // let's update the bounds.
- if (mState == STATE_PIP) {
- resizePinnedStack(STATE_PIP);
+
+ if (mState != STATE_NO_PIP) {
+ movePinnedStack();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipInterpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipInterpolators.java
new file mode 100644
index 000000000000..927c1ec2a888
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipInterpolators.java
@@ -0,0 +1,47 @@
+/*
+ * 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.pip.tv;
+
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * All interpolators needed for TV specific Pip animations
+ */
+public class TvPipInterpolators {
+
+ /**
+ * A standard ease-in-out curve reserved for moments of interaction (button and card states).
+ */
+ public static final Interpolator STANDARD = new PathInterpolator(0.2f, 0.1f, 0f, 1f);
+
+ /**
+ * A sharp ease-out-expo curve created for snappy but fluid browsing between cards and clusters.
+ */
+ public static final Interpolator BROWSE = new PathInterpolator(0.18f, 1f, 0.22f, 1f);
+
+ /**
+ * A smooth ease-out-expo curve created for incoming elements (forward, back, overlay).
+ */
+ public static final Interpolator ENTER = new PathInterpolator(0.12f, 1f, 0.4f, 1f);
+
+ /**
+ * A smooth ease-in-out-expo curve created for outgoing elements (forward, back, overlay).
+ */
+ public static final Interpolator EXIT = new PathInterpolator(0.4f, 1f, 0.12f, 1f);
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
index 6f7cd82f8da0..bda685e99a12 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.pip.tv;
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
@@ -26,7 +24,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
-import android.widget.TextView;
import com.android.wm.shell.R;
@@ -36,12 +33,7 @@ import com.android.wm.shell.R;
*/
public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener {
private final ImageView mIconImageView;
- private final ImageView mButtonImageView;
- private final TextView mDescriptionTextView;
- private Animator mTextFocusGainAnimator;
- private Animator mButtonFocusGainAnimator;
- private Animator mTextFocusLossAnimator;
- private Animator mButtonFocusLossAnimator;
+ private final View mButtonView;
private OnClickListener mOnClickListener;
public TvPipMenuActionButton(Context context) {
@@ -64,8 +56,7 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
inflater.inflate(R.layout.tv_pip_menu_action_button, this);
mIconImageView = findViewById(R.id.icon);
- mButtonImageView = findViewById(R.id.button);
- mDescriptionTextView = findViewById(R.id.desc);
+ mButtonView = findViewById(R.id.button);
final int[] values = new int[]{android.R.attr.src, android.R.attr.text};
final TypedArray typedArray = context.obtainStyledAttributes(attrs, values, defStyleAttr,
@@ -76,43 +67,16 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
if (textResId != 0) {
setTextAndDescription(getContext().getString(textResId));
}
-
typedArray.recycle();
}
@Override
- public void onFinishInflate() {
- super.onFinishInflate();
- mButtonImageView.setOnFocusChangeListener((v, hasFocus) -> {
- if (hasFocus) {
- startFocusGainAnimation();
- } else {
- startFocusLossAnimation();
- }
- });
-
- mTextFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_gain_animation);
- mTextFocusGainAnimator.setTarget(mDescriptionTextView);
- mButtonFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_gain_animation);
- mButtonFocusGainAnimator.setTarget(mButtonImageView);
-
- mTextFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_loss_animation);
- mTextFocusLossAnimator.setTarget(mDescriptionTextView);
- mButtonFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_focus_loss_animation);
- mButtonFocusLossAnimator.setTarget(mButtonImageView);
- }
-
- @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;
- mButtonImageView.setOnClickListener(listener != null ? this : null);
+ mButtonView.setOnClickListener(listener != null ? this : null);
}
@Override
@@ -143,55 +107,16 @@ public class TvPipMenuActionButton extends RelativeLayout implements View.OnClic
* Sets the text for description the with the given string.
*/
public void setTextAndDescription(CharSequence text) {
- mButtonImageView.setContentDescription(text);
- mDescriptionTextView.setText(text);
- }
-
- private static void cancelAnimator(Animator animator) {
- if (animator.isStarted()) {
- animator.cancel();
- }
+ mButtonView.setContentDescription(text);
}
- /**
- * Starts the focus gain animation.
- */
- public void startFocusGainAnimation() {
- cancelAnimator(mButtonFocusLossAnimator);
- cancelAnimator(mTextFocusLossAnimator);
- mTextFocusGainAnimator.start();
- if (mButtonImageView.getAlpha() < 1f) {
- // If we had faded out the ripple drawable, run our manual focus change animation.
- // See the comment at {@link #startFocusLossAnimation()} for the reason of manual
- // animator.
- mButtonFocusGainAnimator.start();
- }
- }
-
- /**
- * Starts the focus loss animation.
- */
- public void startFocusLossAnimation() {
- cancelAnimator(mButtonFocusGainAnimator);
- cancelAnimator(mTextFocusGainAnimator);
- mTextFocusLossAnimator.start();
- if (mButtonImageView.hasFocus()) {
- // Button uses ripple that has the default animation for the focus changes.
- // However, it doesn't expose the API to fade out while it is focused, so we should
- // manually run the fade out animation when PIP controls row loses focus.
- mButtonFocusLossAnimator.start();
- }
+ @Override
+ public void setEnabled(boolean enabled) {
+ mButtonView.setEnabled(enabled);
}
- /**
- * Resets to initial state.
- */
- public void reset() {
- cancelAnimator(mButtonFocusGainAnimator);
- cancelAnimator(mTextFocusGainAnimator);
- cancelAnimator(mButtonFocusLossAnimator);
- cancelAnimator(mTextFocusLossAnimator);
- mButtonImageView.setAlpha(1f);
- mDescriptionTextView.setAlpha(mButtonImageView.hasFocus() ? 1f : 0f);
+ @Override
+ public boolean isEnabled() {
+ return mButtonView.isEnabled();
}
}
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 ee41b41a743d..32861b698daa 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
@@ -24,12 +24,18 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ParceledListSlice;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Handler;
+import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
import androidx.annotation.Nullable;
+import com.android.wm.shell.R;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
@@ -52,11 +58,33 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
private Delegate mDelegate;
private SurfaceControl mLeash;
- private TvPipMenuView mMenuView;
+ private TvPipMenuView mPipMenuView;
+
+ // User can actively move the PiP via the DPAD.
+ private boolean mInMoveMode;
private final List<RemoteAction> mMediaActions = new ArrayList<>();
private final List<RemoteAction> mAppActions = new ArrayList<>();
+ private SyncRtSurfaceTransactionApplier mApplier;
+ RectF mTmpSourceRectF = new RectF();
+ RectF mTmpDestinationRectF = new RectF();
+ Matrix mMoveTransform = new Matrix();
+
+ private final float[] mTmpValues = new float[9];
+ private final Runnable mUpdateEmbeddedMatrix = () -> {
+ if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+ return;
+ }
+ mMoveTransform.getValues(mTmpValues);
+ try {
+ mPipMenuView.getViewRootImpl().getAccessibilityEmbeddedConnection()
+ .setScreenMatrix(mTmpValues);
+ } catch (RemoteException e) {
+ if (DEBUG) e.printStackTrace();
+ }
+ };
+
public TvPipMenuController(Context context, PipBoundsState pipBoundsState,
SystemWindows systemWindows, PipMediaController pipMediaController,
Handler mainHandler) {
@@ -75,7 +103,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
};
context.registerReceiverForAllUsers(closeSystemDialogsBroadcastReceiver,
new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), null /* permission */,
- mainHandler);
+ mainHandler, Context.RECEIVER_EXPORTED);
pipMediaController.addActionListener(this::onMediaActionsChanged);
}
@@ -106,13 +134,13 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
private void attachPipMenuView() {
if (DEBUG) Log.d(TAG, "attachPipMenuView()");
- if (mMenuView != null) {
+ if (mPipMenuView != null) {
detachPipMenuView();
}
- mMenuView = new TvPipMenuView(mContext);
- mMenuView.setListener(this);
- mSystemWindows.addView(mMenuView,
+ mPipMenuView = new TvPipMenuView(mContext);
+ mPipMenuView.setListener(this);
+ mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
}
@@ -121,56 +149,83 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
public void showMenu() {
if (DEBUG) Log.d(TAG, "showMenu()");
- if (mMenuView != null) {
- mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE,
- mPipBoundsState.getDisplayBounds().width(),
- mPipBoundsState.getDisplayBounds().height()));
+ if (mPipMenuView != null) {
+ Rect menuBounds = getMenuBounds(mPipBoundsState.getBounds());
+ mSystemWindows.updateViewLayout(mPipMenuView, getPipMenuLayoutParams(
+ MENU_WINDOW_TITLE, menuBounds.width(), menuBounds.height()));
maybeUpdateMenuViewActions();
- mMenuView.show();
- // By default, SystemWindows views are above everything else.
- // Set the relative z-order so the menu is below PiP.
- if (mMenuView.getWindowSurfaceControl() != null && mLeash != null) {
+ SurfaceControl menuSurfaceControl = mSystemWindows.getViewSurface(mPipMenuView);
+ if (menuSurfaceControl != null) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.setRelativeLayer(mMenuView.getWindowSurfaceControl(), mLeash, -1);
+ t.setRelativeLayer(mPipMenuView.getWindowSurfaceControl(), mLeash, 1);
+ t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top);
t.apply();
}
+ mPipMenuView.show(mInMoveMode, mDelegate.getPipGravity());
}
}
- void hideMenu() {
- hideMenu(true);
+ void updateMenu(int gravity) {
+ mPipMenuView.showMovementHints(gravity);
}
- void hideMenu(boolean movePipWindow) {
- if (DEBUG) Log.d(TAG, "hideMenu(), movePipWindow=" + movePipWindow);
+ private Rect getMenuBounds(Rect pipBounds) {
+ int extraSpaceInPx = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.pip_menu_outer_space);
+ Rect menuBounds = new Rect(pipBounds);
+ menuBounds.inset(-extraSpaceInPx, -extraSpaceInPx);
+ return menuBounds;
+ }
+ void hideMenu() {
if (!isMenuVisible()) {
+ if (DEBUG) Log.d(TAG, "hideMenu() - Menu isn't visible, so don't hide");
return;
+ } else {
+ if (DEBUG) Log.d(TAG, "hideMenu()");
}
- mMenuView.hide();
- if (movePipWindow) {
- mDelegate.movePipToNormalPosition();
+ mPipMenuView.hide(mInMoveMode);
+ if (!mInMoveMode) {
+ mDelegate.closeMenu();
}
}
@Override
- public void detach() {
- hideMenu();
- detachPipMenuView();
- mLeash = null;
+ public void onEnterMoveMode() {
+ if (DEBUG) Log.d(TAG, "onEnterMoveMode - " + mInMoveMode);
+ mInMoveMode = true;
+ mPipMenuView.showMenuButtons(false);
+ mPipMenuView.showMovementHints(mDelegate.getPipGravity());
}
- private void detachPipMenuView() {
- if (DEBUG) Log.d(TAG, "detachPipMenuView()");
+ @Override
+ public boolean onExitMoveMode() {
+ if (DEBUG) Log.d(TAG, "onExitMoveMode - " + mInMoveMode);
+ if (mInMoveMode) {
+ mInMoveMode = false;
+ mPipMenuView.showMenuButtons(true);
+ mPipMenuView.hideMovementHints();
+ return true;
+ }
+ return false;
+ }
- if (mMenuView == null) {
- return;
+ @Override
+ public boolean onPipMovement(int keycode) {
+ if (DEBUG) Log.d(TAG, "onPipMovement - " + mInMoveMode);
+ if (mInMoveMode) {
+ mDelegate.movePip(keycode);
}
+ return mInMoveMode;
+ }
- mSystemWindows.removeView(mMenuView);
- mMenuView = null;
+ @Override
+ public void detach() {
+ hideMenu();
+ detachPipMenuView();
+ mLeash = null;
}
@Override
@@ -181,7 +236,15 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
private void onMediaActionsChanged(List<RemoteAction> actions) {
if (DEBUG) Log.d(TAG, "onMediaActionsChanged()");
- updateAdditionalActionsList(mMediaActions, actions);
+
+ // Hide disabled actions.
+ List<RemoteAction> enabledActions = new ArrayList<>();
+ for (RemoteAction remoteAction : actions) {
+ if (remoteAction.isEnabled()) {
+ enabledActions.add(remoteAction);
+ }
+ }
+ updateAdditionalActionsList(mMediaActions, enabledActions);
}
private void updateAdditionalActionsList(
@@ -200,24 +263,146 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
private void maybeUpdateMenuViewActions() {
- if (mMenuView == null) {
+ if (mPipMenuView == null) {
return;
}
if (!mAppActions.isEmpty()) {
- mMenuView.setAdditionalActions(mAppActions, mMainHandler);
+ mPipMenuView.setAdditionalActions(mAppActions, mMainHandler);
} else {
- mMenuView.setAdditionalActions(mMediaActions, mMainHandler);
+ mPipMenuView.setAdditionalActions(mMediaActions, mMainHandler);
}
}
@Override
public boolean isMenuVisible() {
- return mMenuView != null && mMenuView.isVisible();
+ boolean isVisible = mPipMenuView != null && mPipMenuView.isVisible();
+ if (DEBUG) Log.d(TAG, "isMenuVisible: " + isVisible);
+ return isVisible;
+ }
+
+ /**
+ * Does an immediate window crop of the PiP menu.
+ */
+ @Override
+ public void resizePipMenu(@android.annotation.Nullable SurfaceControl pipLeash,
+ @android.annotation.Nullable SurfaceControl.Transaction t,
+ Rect destinationBounds) {
+ if (DEBUG) Log.d(TAG, "resizePipMenu: " + destinationBounds.toShortString());
+ if (destinationBounds.isEmpty()) {
+ return;
+ }
+
+ if (!maybeCreateSyncApplier()) {
+ return;
+ }
+
+ SurfaceControl surfaceControl = getSurfaceControl();
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(surfaceControl)
+ .withWindowCrop(getMenuBounds(destinationBounds))
+ .build();
+ if (pipLeash != null && t != null) {
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ pipParams = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash)
+ .withMergeTransaction(t)
+ .build();
+ mApplier.scheduleApply(params, pipParams);
+ } else {
+ mApplier.scheduleApply(params);
+ }
+ }
+
+ private SurfaceControl getSurfaceControl() {
+ return mSystemWindows.getViewSurface(mPipMenuView);
+ }
+
+ @Override
+ public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction transaction,
+ Rect pipDestBounds) {
+ if (DEBUG) Log.d(TAG, "movePipMenu: " + pipDestBounds.toShortString());
+
+ if (pipDestBounds.isEmpty()) {
+ if (transaction == null && DEBUG) Log.d(TAG, "no transaction given");
+ return;
+ }
+ if (!maybeCreateSyncApplier()) {
+ return;
+ }
+
+ Rect menuDestBounds = getMenuBounds(pipDestBounds);
+ Rect mTmpSourceBounds = new Rect();
+ // If there is no pip leash supplied, that means the PiP leash is already finalized
+ // 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) Log.d(TAG, "mTmpSourceBounds based on mPipMenuView.getBoundsOnScreen()");
+ mPipMenuView.getBoundsOnScreen(mTmpSourceBounds);
+ } else {
+ if (DEBUG) Log.d(TAG, "mTmpSourceBounds based on menu width and height");
+ mTmpSourceBounds.set(0, 0, menuDestBounds.width(), menuDestBounds.height());
+ }
+
+ mTmpSourceRectF.set(mTmpSourceBounds);
+ mTmpDestinationRectF.set(menuDestBounds);
+ mMoveTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
+
+ SurfaceControl surfaceControl = getSurfaceControl();
+ SyncRtSurfaceTransactionApplier.SurfaceParams params =
+ new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
+ surfaceControl).withMatrix(mMoveTransform).build();
+
+ if (pipLeash != null && transaction != null) {
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ pipParams = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash)
+ .withMergeTransaction(transaction)
+ .build();
+ mApplier.scheduleApply(params, pipParams);
+ } else {
+ mApplier.scheduleApply(params);
+ }
+
+ if (mPipMenuView.getViewRootImpl() != null) {
+ mPipMenuView.getHandler().removeCallbacks(mUpdateEmbeddedMatrix);
+ mPipMenuView.getHandler().post(mUpdateEmbeddedMatrix);
+ }
+ }
+
+ private boolean maybeCreateSyncApplier() {
+ if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+ Log.v(TAG, "Not going to move PiP, either menu or its parent is not created.");
+ return false;
+ }
+
+ if (mApplier == null) {
+ mApplier = new SyncRtSurfaceTransactionApplier(mPipMenuView);
+ }
+ return true;
+ }
+
+ private void detachPipMenuView() {
+ if (mPipMenuView == null) {
+ return;
+ }
+
+ mApplier = null;
+ mSystemWindows.removeView(mPipMenuView);
+ mPipMenuView = null;
+ }
+
+ @Override
+ public void updateMenuBounds(Rect destinationBounds) {
+ Rect menuBounds = getMenuBounds(destinationBounds);
+ if (DEBUG) Log.d(TAG, "updateMenuBounds: " + menuBounds.toShortString());
+ mSystemWindows.updateViewLayout(mPipMenuView,
+ getPipMenuLayoutParams(MENU_WINDOW_TITLE, menuBounds.width(),
+ menuBounds.height()));
}
@Override
public void onBackPress() {
- hideMenu();
+ if (!onExitMoveMode()) {
+ hideMenu();
+ }
}
@Override
@@ -231,8 +416,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
interface Delegate {
- void movePipToNormalPosition();
void movePipToFullscreen();
+
+ void movePip(int keycode);
+
+ int getPipGravity();
+
+ void closeMenu();
+
void closePip();
}
}
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 d6cd9ea13ca1..0141b6a1859e 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
@@ -16,19 +16,21 @@
package com.android.wm.shell.pip.tv;
-import static android.animation.AnimatorInflater.loadAnimator;
import static android.view.KeyEvent.ACTION_UP;
import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.KeyEvent.KEYCODE_DPAD_CENTER;
+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 android.animation.Animator;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.Context;
-import android.graphics.Color;
import android.os.Handler;
-import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
@@ -36,6 +38,7 @@ import android.view.View;
import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
@@ -47,23 +50,26 @@ import java.util.ArrayList;
import java.util.List;
/**
- * A View that represents Pip Menu on TV. It's responsible for displaying 2 ever-present Pip Menu
- * actions: Fullscreen 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 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.
*/
public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
private static final String TAG = "TvPipMenuView";
private static final boolean DEBUG = TvPipController.DEBUG;
- private static final float DISABLED_ACTION_ALPHA = 0.54f;
-
- private final Animator mFadeInAnimation;
- private final Animator mFadeOutAnimation;
- @Nullable private Listener mListener;
+ @Nullable
+ private Listener mListener;
private final LinearLayout mActionButtonsContainer;
+ private final View mMenuFrameView;
private final List<TvPipMenuActionButton> mAdditionalButtons = new ArrayList<>();
+ private final ImageView mArrowUp;
+ private final ImageView mArrowRight;
+ private final ImageView mArrowDown;
+ private final ImageView mArrowLeft;
+
public TvPipMenuView(@NonNull Context context) {
this(context, null);
}
@@ -87,36 +93,68 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
.setOnClickListener(this);
mActionButtonsContainer.findViewById(R.id.tv_pip_menu_close_button)
.setOnClickListener(this);
+ mActionButtonsContainer.findViewById(R.id.tv_pip_menu_move_button)
+ .setOnClickListener(this);
- mFadeInAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_in_animation);
- mFadeInAnimation.setTarget(mActionButtonsContainer);
+ mMenuFrameView = findViewById(R.id.tv_pip_menu_frame);
- mFadeOutAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_out_animation);
- mFadeOutAnimation.setTarget(mActionButtonsContainer);
+ 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);
}
void setListener(@Nullable Listener listener) {
mListener = listener;
}
- void show() {
- if (DEBUG) Log.d(TAG, "show()");
-
- mFadeInAnimation.start();
- setAlpha(1.0f);
+ void show(boolean inMoveMode, int gravity) {
+ if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode);
grantWindowFocus(true);
+
+ if (inMoveMode) {
+ showMovementHints(gravity);
+ } else {
+ animateAlphaTo(1, mActionButtonsContainer);
+ }
+ animateAlphaTo(1, mMenuFrameView);
}
- void hide() {
+ void hide(boolean isInMoveMode) {
if (DEBUG) Log.d(TAG, "hide()");
+ animateAlphaTo(0, mActionButtonsContainer);
+ animateAlphaTo(0, mMenuFrameView);
+ hideMovementHints();
+
+ if (!isInMoveMode) {
+ grantWindowFocus(false);
+ }
+ }
- mFadeOutAnimation.start();
- setAlpha(0.0f);
- grantWindowFocus(false);
+ private void animateAlphaTo(float alpha, View view) {
+ view.animate()
+ .alpha(alpha)
+ .setInterpolator(alpha == 0f ? TvPipInterpolators.EXIT : TvPipInterpolators.ENTER)
+ .setDuration(500)
+ .withStartAction(() -> {
+ if (alpha != 0) {
+ view.setVisibility(VISIBLE);
+ }
+ })
+ .withEndAction(() -> {
+ if (alpha == 0) {
+ view.setVisibility(GONE);
+ }
+ });
}
boolean isVisible() {
- return getAlpha() == 1.0f;
+ return mMenuFrameView.getAlpha() != 0f
+ || mActionButtonsContainer.getAlpha() != 0f
+ || mArrowUp.getAlpha() != 0f
+ || mArrowRight.getAlpha() != 0f
+ || mArrowDown.getAlpha() != 0f
+ || mArrowLeft.getAlpha() != 0f;
}
private void grantWindowFocus(boolean grantFocus) {
@@ -140,9 +178,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
// Add buttons until we have enough to display all of the actions.
while (actionsNumber > buttonsNumber) {
- final TvPipMenuActionButton button = (TvPipMenuActionButton) layoutInflater.inflate(
- R.layout.tv_pip_menu_additional_action_button, mActionButtonsContainer,
- false);
+ TvPipMenuActionButton button = new TvPipMenuActionButton(mContext);
button.setOnClickListener(this);
mActionButtonsContainer.addView(button);
@@ -168,13 +204,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
button.setVisibility(View.VISIBLE); // Ensure the button is visible.
button.setTextAndDescription(action.getContentDescription());
button.setEnabled(action.isEnabled());
- button.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
button.setTag(action);
-
- action.getIcon().loadDrawableAsync(mContext, drawable -> {
- drawable.setTint(Color.WHITE);
- button.setImageDrawable(drawable);
- }, mainHandler);
+ action.getIcon().loadDrawableAsync(mContext, button::setImageDrawable, mainHandler);
}
}
@@ -198,6 +229,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
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 {
@@ -217,17 +250,79 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK
- && mListener != null) {
- mListener.onBackPress();
- return true;
+ if (DEBUG) {
+ Log.d(TAG, "dispatchKeyEvent, action: " + event.getAction()
+ + ", keycode: " + event.getKeyCode());
+ }
+ if (mListener != null && event.getAction() == ACTION_UP) {
+ switch (event.getKeyCode()) {
+ case KEYCODE_BACK:
+ mListener.onBackPress();
+ return true;
+ case KEYCODE_DPAD_UP:
+ case KEYCODE_DPAD_DOWN:
+ case KEYCODE_DPAD_LEFT:
+ case KEYCODE_DPAD_RIGHT:
+ return mListener.onPipMovement(event.getKeyCode()) || super.dispatchKeyEvent(
+ event);
+ case KEYCODE_DPAD_CENTER:
+ return mListener.onExitMoveMode() || super.dispatchKeyEvent(event);
+ default:
+ break;
+ }
}
return super.dispatchKeyEvent(event);
}
+ /**
+ * Shows user hints for moving the PiP, e.g. arrows.
+ */
+ public void showMovementHints(int gravity) {
+ if (DEBUG) Log.d(TAG, "showMovementHints(), position: " + Gravity.toString(gravity));
+
+ animateAlphaTo((gravity & Gravity.BOTTOM) == Gravity.BOTTOM ? 1f : 0f, mArrowUp);
+ animateAlphaTo((gravity & Gravity.TOP) == Gravity.TOP ? 1f : 0f, mArrowDown);
+ animateAlphaTo((gravity & Gravity.RIGHT) == Gravity.RIGHT ? 1f : 0f, mArrowLeft);
+ animateAlphaTo((gravity & Gravity.LEFT) == Gravity.LEFT ? 1f : 0f, mArrowRight);
+ }
+
+ /**
+ * Hides user hints for moving the PiP, e.g. arrows.
+ */
+ public void hideMovementHints() {
+ if (DEBUG) Log.d(TAG, "hideMovementHints()");
+ animateAlphaTo(0, mArrowUp);
+ animateAlphaTo(0, mArrowRight);
+ animateAlphaTo(0, mArrowDown);
+ animateAlphaTo(0, mArrowLeft);
+ }
+
+ /**
+ * Show or hide the pip user actions.
+ */
+ public void showMenuButtons(boolean show) {
+ if (DEBUG) Log.d(TAG, "showMenuButtons: " + show);
+ animateAlphaTo(show ? 1 : 0, mActionButtonsContainer);
+ }
+
interface Listener {
+
void onBackPress();
+
+ void onEnterMoveMode();
+
+ /**
+ * @return whether move mode was exited
+ */
+ boolean onExitMoveMode();
+
+ /**
+ * @return whether pip movement was handled.
+ */
+ boolean onPipMovement(int keycode);
+
void onCloseButtonClick();
+
void onFullscreenButtonClick();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index 551476dc9d54..5062cc436461 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -29,7 +29,6 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipTransitionController;
@@ -42,11 +41,11 @@ import com.android.wm.shell.transition.Transitions;
public class TvPipTransition extends PipTransitionController {
public TvPipTransition(PipBoundsState pipBoundsState,
PipMenuController pipMenuController,
- PipBoundsAlgorithm pipBoundsAlgorithm,
+ TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
PipAnimationController pipAnimationController,
Transitions transitions,
@NonNull ShellTaskOrganizer shellTaskOrganizer) {
- super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, pipAnimationController,
+ super(pipBoundsState, pipMenuController, tvPipBoundsAlgorithm, pipAnimationController,
transitions, shellTaskOrganizer);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 963a3dc70262..1ddc0e7abeaf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -32,6 +32,12 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM_SHELL),
WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
+ WM_SHELL_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM_STARTING_WINDOW),
+ WM_SHELL_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ "ShellBackPreview"),
+ WM_SHELL_RECENT_TASKS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM_SHELL),
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
private final boolean mEnabled;
@@ -91,6 +97,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
private static class Consts {
private static final String TAG_WM_SHELL = "WindowManagerShell";
+ private static final String TAG_WM_STARTING_WINDOW = "ShellStartingWindow";
private static final boolean ENABLE_DEBUG = true;
private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 338c944f7eec..c166178e9bbd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -34,6 +34,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
@@ -41,6 +42,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.StagedSplitBounds;
@@ -128,6 +130,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
mTaskSplitBoundsMap.put(taskId1, splitBounds);
mTaskSplitBoundsMap.put(taskId2, splitBounds);
notifyRecentTasksChanged();
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Add split pair: %d, %d, %s",
+ taskId1, taskId2, splitBounds);
}
/**
@@ -141,6 +145,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
mTaskSplitBoundsMap.remove(taskId);
mTaskSplitBoundsMap.remove(pairedTaskId);
notifyRecentTasksChanged();
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Remove split pair: %d, %d",
+ taskId, pairedTaskId);
}
}
@@ -182,6 +188,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
@VisibleForTesting
void notifyRecentTasksChanged() {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed");
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).run();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 3cfa541c1c86..d022ec15f232 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -89,9 +89,17 @@ interface ISplitScreen {
/**
* Version of startTasks using legacy transition system.
*/
- oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
- int sideTaskId, in Bundle sideOptions, int sidePosition,
- float splitRatio, in RemoteAnimationAdapter adapter) = 11;
+ oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
+ int sideTaskId, in Bundle sideOptions, int sidePosition,
+ float splitRatio, in RemoteAnimationAdapter adapter) = 11;
+
+ /**
+ * Start a pair of intent and task using legacy transition system.
+ */
+ oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
+ in Intent fillInIntent, int taskId, boolean intentFirst, in Bundle mainOptions,
+ in Bundle sideOptions, int sidePosition, float splitRatio,
+ in RemoteAnimationAdapter adapter) = 12;
/**
* Blocking call that notifies and gets additional split-screen targets when entering
@@ -100,5 +108,7 @@ interface ISplitScreen {
* @param appTargets apps that will be re-parented to display area
*/
RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
- in RemoteAnimationTarget[] appTargets) = 12;
+ in RemoteAnimationTarget[] appTargets) = 13;
+
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java
new file mode 100644
index 000000000000..8e5cc6d47edd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java
@@ -0,0 +1,182 @@
+/*
+ * 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.splitscreen;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+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_APPLICATION_OVERLAY;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Binder;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.SurfaceUtils;
+
+/**
+ * Handles split decor like showing resizing hint for a specific split.
+ */
+class SplitDecorManager extends WindowlessWindowManager {
+ private static final String TAG = SplitDecorManager.class.getSimpleName();
+ private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
+
+ private final IconProvider mIconProvider;
+ private final SurfaceSession mSurfaceSession;
+
+ private Drawable mIcon;
+ private ImageView mResizingIconView;
+ private SurfaceControlViewHost mViewHost;
+ private SurfaceControl mHostLeash;
+ private SurfaceControl mIconLeash;
+ private SurfaceControl mBackgroundLeash;
+
+ SplitDecorManager(Configuration configuration, IconProvider iconProvider,
+ SurfaceSession surfaceSession) {
+ super(configuration, null /* rootSurface */, null /* hostInputToken */);
+ mIconProvider = iconProvider;
+ mSurfaceSession = surfaceSession;
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName(TAG)
+ .setHidden(true)
+ .setParent(mHostLeash)
+ .setCallsite("SplitDecorManager#attachToParentSurface");
+ mIconLeash = builder.build();
+ b.setParent(mIconLeash);
+ }
+
+ void inflate(Context context, SurfaceControl rootLeash, Rect rootBounds) {
+ if (mIconLeash != null && mViewHost != null) {
+ return;
+ }
+
+ context = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
+ null /* options */);
+ mHostLeash = rootLeash;
+ mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this);
+
+ final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context)
+ .inflate(R.layout.split_decor, null);
+ mResizingIconView = rootLayout.findViewById(R.id.split_resizing_icon);
+
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
+ lp.width = rootBounds.width();
+ lp.height = rootBounds.height();
+ lp.token = new Binder();
+ lp.setTitle(TAG);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
+ // TRUSTED_OVERLAY for windowless window without input channel.
+ mViewHost.setView(rootLayout, lp);
+ }
+
+ void release(SurfaceControl.Transaction t) {
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+ if (mIconLeash != null) {
+ t.remove(mIconLeash);
+ mIconLeash = null;
+ }
+ if (mBackgroundLeash != null) {
+ t.remove(mBackgroundLeash);
+ mBackgroundLeash = null;
+ }
+ mHostLeash = null;
+ mIcon = null;
+ mResizingIconView = null;
+ }
+
+ /** Showing resizing hint. */
+ void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
+ SurfaceControl.Transaction t) {
+ if (mResizingIconView == null) {
+ return;
+ }
+
+ if (mIcon == null) {
+ // TODO: add fade-in animation.
+ mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
+ RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
+ t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
+ .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1)
+ .show(mBackgroundLeash);
+
+ mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
+ mResizingIconView.setImageDrawable(mIcon);
+ mResizingIconView.setVisibility(View.VISIBLE);
+
+ WindowManager.LayoutParams lp =
+ (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
+ lp.width = mIcon.getIntrinsicWidth();
+ lp.height = mIcon.getIntrinsicHeight();
+ mViewHost.relayout(lp);
+ t.show(mIconLeash).setLayer(mIconLeash, SPLIT_DIVIDER_LAYER);
+ }
+
+ t.setPosition(mIconLeash,
+ newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2,
+ newBounds.height() / 2 - mIcon.getIntrinsicWidth() / 2);
+ }
+
+ /** Stops showing resizing hint. */
+ void onResized(Rect newBounds, SurfaceControl.Transaction t) {
+ if (mResizingIconView == null) {
+ return;
+ }
+
+ if (mIcon != null) {
+ mResizingIconView.setVisibility(View.GONE);
+ mResizingIconView.setImageDrawable(null);
+ t.remove(mBackgroundLeash).hide(mIconLeash);
+ mIcon = null;
+ mBackgroundLeash = null;
+ }
+ }
+
+ private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
+ final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
+ return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index a91dfe1c13e2..448773ae9ea2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -76,12 +76,6 @@ public interface SplitScreen {
}
/**
- * Called when the keyguard occluded state changes.
- * @param occluded Indicates if the keyguard is now occluded.
- */
- void onKeyguardOccludedChanged(boolean occluded);
-
- /**
* Called when the visibility of the keyguard changes.
* @param showing Indicates if the keyguard is now visible.
*/
@@ -90,9 +84,6 @@ public interface SplitScreen {
/** Called when device waking up finished. */
void onFinishedWakingUp();
- /** Called when device going to sleep finished. */
- void onFinishedGoingToSleep();
-
/** Get a string representation of a stage type */
static String stageTypeToString(@StageType int stage) {
switch (stage) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 4c77f6a7e00d..990b53a601f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -26,6 +26,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
+import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -58,6 +59,7 @@ import com.android.internal.logging.InstanceId;
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.RemoteCallable;
@@ -124,6 +126,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
private final ShellExecutor mMainExecutor;
private final SplitScreenImpl mImpl = new SplitScreenImpl();
+ private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
private final DisplayInsetsController mDisplayInsetsController;
private final Transitions mTransitions;
@@ -141,7 +144,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, Context context,
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
- ShellExecutor mainExecutor, DisplayImeController displayImeController,
+ ShellExecutor mainExecutor, DisplayController displayController,
+ DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
@@ -151,6 +155,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mContext = context;
mRootTDAOrganizer = rootTDAOrganizer;
mMainExecutor = mainExecutor;
+ mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
mTransitions = transitions;
@@ -179,7 +184,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
if (mStageCoordinator == null) {
// TODO: Multi-display
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
- mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
+ mRootTDAOrganizer, mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider);
}
@@ -237,6 +242,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
}
+ public void prepareEnterSplitScreen(WindowContainerTransaction wct,
+ ActivityManager.RunningTaskInfo taskInfo, int startPosition) {
+ mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition);
+ }
+
+ public void finishEnterSplitScreen(SurfaceControl.Transaction t) {
+ mStageCoordinator.finishEnterSplitScreen(t);
+ }
+
public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE;
final int stagePosition =
@@ -248,10 +262,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
}
- public void onKeyguardOccludedChanged(boolean occluded) {
- mStageCoordinator.onKeyguardOccludedChanged(occluded);
- }
-
public void onKeyguardVisibilityChanged(boolean showing) {
mStageCoordinator.onKeyguardVisibilityChanged(showing);
}
@@ -260,10 +270,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.onFinishedWakingUp();
}
- public void onFinishedGoingToSleep() {
- mStageCoordinator.onFinishedGoingToSleep();
- }
-
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
}
@@ -317,12 +323,19 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
- if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
+ if (!ENABLE_SHELL_TRANSITIONS) {
startIntentLegacy(intent, fillInIntent, position, options);
return;
}
- mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options,
- null /* remote */);
+
+ try {
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+ null /* wct */);
+ intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */,
+ null /* requiredPermission */, options);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.e(TAG, "Failed to launch task", e);
+ }
}
private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
@@ -366,7 +379,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) {
- if (apps.length < 2) return null;
+ if (ENABLE_SHELL_TRANSITIONS || apps.length < 2) return null;
+ // TODO(b/206487881): Integrate this with shell transition.
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
if (mSplitTasksContainerLayer != null) {
// Remove the previous layer before recreating
@@ -487,13 +501,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
- public void onKeyguardOccludedChanged(boolean occluded) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.onKeyguardOccludedChanged(occluded);
- });
- }
-
- @Override
public void registerSplitScreenListener(SplitScreenListener listener, Executor executor) {
if (mExecutors.containsKey(listener)) return;
@@ -534,13 +541,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
SplitScreenController.this.onFinishedWakingUp();
});
}
-
- @Override
- public void onFinishedGoingToSleep() {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.onFinishedGoingToSleep();
- });
- }
}
/**
@@ -641,6 +641,18 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
+ public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
+ Intent fillInIntent, int taskId, boolean intentFirst, Bundle mainOptions,
+ Bundle sideOptions, int sidePosition, float splitRatio,
+ RemoteAnimationAdapter adapter) {
+ executeRemoteCallWithTaskPermission(mController,
+ "startIntentAndTaskWithLegacyTransition", (controller) ->
+ controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition(
+ pendingIntent, fillInIntent, taskId, intentFirst, mainOptions,
+ sideOptions, sidePosition, splitRatio, adapter));
+ }
+
+ @Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 86e7b0e4cb7f..d30d0cc95f46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -23,8 +23,13 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
+import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
-import static com.android.wm.shell.transition.Transitions.isOpeningType;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -39,8 +44,11 @@ import android.window.RemoteTransition;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.OneShotRemoteHandler;
import com.android.wm.shell.transition.Transitions;
@@ -57,30 +65,29 @@ class SplitScreenTransitions {
private final Transitions mTransitions;
private final Runnable mOnFinish;
- IBinder mPendingDismiss = null;
+ DismissTransition mPendingDismiss = null;
IBinder mPendingEnter = null;
+ IBinder mPendingRecent = null;
private IBinder mAnimatingTransition = null;
- private OneShotRemoteHandler mRemoteHandler = null;
+ private OneShotRemoteHandler mPendingRemoteHandler = null;
+ private OneShotRemoteHandler mActiveRemoteHandler = null;
- private Transitions.TransitionFinishCallback mRemoteFinishCB = (wct, wctCB) -> {
- if (wct != null || wctCB != null) {
- throw new UnsupportedOperationException("finish transactions not supported yet.");
- }
- onFinish();
- };
+ private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
/** Keeps track of currently running animations */
private final ArrayList<Animator> mAnimations = new ArrayList<>();
+ private final StageCoordinator mStageCoordinator;
private Transitions.TransitionFinishCallback mFinishCallback = null;
private SurfaceControl.Transaction mFinishTransaction;
SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
- @NonNull Runnable onFinishCallback) {
+ @NonNull Runnable onFinishCallback, StageCoordinator stageCoordinator) {
mTransactionPool = pool;
mTransitions = transitions;
mOnFinish = onFinishCallback;
+ mStageCoordinator = stageCoordinator;
}
void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@@ -90,10 +97,11 @@ class SplitScreenTransitions {
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
mFinishCallback = finishCallback;
mAnimatingTransition = transition;
- if (mRemoteHandler != null) {
- mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction,
- mRemoteFinishCB);
- mRemoteHandler = null;
+ if (mPendingRemoteHandler != null) {
+ mPendingRemoteHandler.startAnimation(transition, info, startTransaction,
+ finishTransaction, mRemoteFinishCB);
+ mActiveRemoteHandler = mPendingRemoteHandler;
+ mPendingRemoteHandler = null;
return;
}
playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
@@ -127,12 +135,6 @@ class SplitScreenTransitions {
}
// TODO(shell-transitions): screenshot here
final Rect startBounds = new Rect(change.getStartAbsBounds());
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Dismissing split via snap which means the still-visible task has been
- // dragged to its end position at animation start so reflect that here.
- startBounds.offsetTo(change.getEndAbsBounds().left,
- change.getEndAbsBounds().top);
- }
final Rect endBounds = new Rect(change.getEndAbsBounds());
startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
@@ -144,10 +146,11 @@ class SplitScreenTransitions {
if (transition == mPendingEnter && (mainRoot.equals(change.getContainer())
|| sideRoot.equals(change.getContainer()))) {
- t.setWindowCrop(leash, change.getStartAbsBounds().width(),
- change.getStartAbsBounds().height());
+ t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top);
+ t.setWindowCrop(leash, change.getEndAbsBounds().width(),
+ change.getEndAbsBounds().height());
}
- boolean isOpening = isOpeningType(info.getType());
+ boolean isOpening = isOpeningTransition(info);
if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
// fade in
startExampleAnimation(leash, true /* show */);
@@ -164,36 +167,69 @@ class SplitScreenTransitions {
}
}
t.apply();
- onFinish();
+ onFinish(null /* wct */, null /* wctCB */);
}
/** Starts a transition to enter split with a remote transition animator. */
IBinder startEnterTransition(@WindowManager.TransitionType int transitType,
@NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition,
@NonNull Transitions.TransitionHandler handler) {
+ final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
+ mPendingEnter = transition;
+
if (remoteTransition != null) {
// Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
- mRemoteHandler = new OneShotRemoteHandler(
+ mPendingRemoteHandler = new OneShotRemoteHandler(
mTransitions.getMainExecutor(), remoteTransition);
+ mPendingRemoteHandler.setTransition(transition);
}
- final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
- mPendingEnter = transition;
- if (mRemoteHandler != null) {
- mRemoteHandler.setTransition(transition);
+ return transition;
+ }
+
+ /** Starts a transition to dismiss split. */
+ IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
+ Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
+ @SplitScreenController.ExitReason int reason) {
+ final int type = reason == EXIT_REASON_DRAG_DIVIDER
+ ? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
+ if (transition == null) {
+ transition = mTransitions.startTransition(type, wct, handler);
}
+ mPendingDismiss = new DismissTransition(transition, reason, dismissTop);
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ + " deduced Dismiss due to %s. toTop=%s",
+ exitReasonToString(reason), stageTypeToString(dismissTop));
return transition;
}
- /** Starts a transition for dismissing split after dragging the divider to a screen edge */
- IBinder startSnapToDismiss(@NonNull WindowContainerTransaction wct,
- @NonNull Transitions.TransitionHandler handler) {
- final IBinder transition = mTransitions.startTransition(
- TRANSIT_SPLIT_DISMISS_SNAP, wct, handler);
- mPendingDismiss = transition;
+ IBinder startRecentTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
+ Transitions.TransitionHandler handler, @Nullable RemoteTransition remoteTransition) {
+ if (transition == null) {
+ transition = mTransitions.startTransition(TRANSIT_OPEN, wct, handler);
+ }
+ mPendingRecent = transition;
+
+ if (remoteTransition != null) {
+ // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
+ mPendingRemoteHandler = new OneShotRemoteHandler(
+ mTransitions.getMainExecutor(), remoteTransition);
+ mPendingRemoteHandler.setTransition(transition);
+ }
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ + " deduced Enter recent panel");
return transition;
}
- void onFinish() {
+ void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
+ IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
+ if (mergeTarget == mAnimatingTransition && mActiveRemoteHandler != null) {
+ mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ }
+ }
+
+ void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) {
if (!mAnimations.isEmpty()) return;
mOnFinish.run();
if (mFinishTransaction != null) {
@@ -201,14 +237,25 @@ class SplitScreenTransitions {
mTransactionPool.release(mFinishTransaction);
mFinishTransaction = null;
}
- mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
- mFinishCallback = null;
+ if (mFinishCallback != null) {
+ mFinishCallback.onTransitionFinished(wct /* wct */, wctCB /* wctCB */);
+ mFinishCallback = null;
+ }
if (mAnimatingTransition == mPendingEnter) {
mPendingEnter = null;
}
- if (mAnimatingTransition == mPendingDismiss) {
+ if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
mPendingDismiss = null;
}
+ if (mAnimatingTransition == mPendingRecent) {
+ // If the wct is not null while finishing recent transition, it indicates it's not
+ // dismissing split and thus need to reorder split task so they can be on top again.
+ final boolean dismissSplit = wct == null;
+ mStageCoordinator.finishRecentAnimation(dismissSplit);
+ mPendingRecent = null;
+ }
+ mPendingRemoteHandler = null;
+ mActiveRemoteHandler = null;
mAnimatingTransition = null;
}
@@ -230,7 +277,7 @@ class SplitScreenTransitions {
mTransactionPool.release(transaction);
mTransitions.getMainExecutor().execute(() -> {
mAnimations.remove(va);
- onFinish();
+ onFinish(null /* wct */, null /* wctCB */);
});
};
va.addListener(new Animator.AnimatorListener() {
@@ -278,7 +325,7 @@ class SplitScreenTransitions {
mTransactionPool.release(transaction);
mTransitions.getMainExecutor().execute(() -> {
mAnimations.remove(va);
- onFinish();
+ onFinish(null /* wct */, null /* wctCB */);
});
};
va.addListener(new AnimatorListenerAdapter() {
@@ -295,4 +342,26 @@ class SplitScreenTransitions {
mAnimations.add(va);
mTransitions.getAnimExecutor().execute(va::start);
}
+
+ private boolean isOpeningTransition(TransitionInfo info) {
+ return Transitions.isOpeningType(info.getType())
+ || info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE
+ || info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
+ }
+
+ /** Bundled information of dismiss transition. */
+ static class DismissTransition {
+ IBinder mTransition;
+
+ int mReason;
+
+ @SplitScreen.StageType
+ int mDismissTop;
+
+ DismissTransition(IBinder transition, int reason, int dismissTop) {
+ this.mTransition = transition;
+ this.mReason = reason;
+ this.mDismissTop = dismissTop;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 38c1aff0a62c..219530b46da4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -18,9 +18,13 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
@@ -32,7 +36,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
-import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
@@ -40,10 +43,10 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
import static com.android.wm.shell.transition.Transitions.isClosingType;
@@ -58,6 +61,7 @@ import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
@@ -85,8 +89,10 @@ import com.android.internal.protolog.common.ProtoLog;
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.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
@@ -120,13 +126,11 @@ import javax.inject.Provider;
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
*/
class StageCoordinator implements SplitLayout.SplitLayoutHandler,
- RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
+ RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener,
+ DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler {
private static final String TAG = StageCoordinator.class.getSimpleName();
- /** internal value for mDismissTop that represents no dismiss */
- private static final int NO_DISMISS = -2;
-
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final MainStage mMainStage;
@@ -135,6 +139,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private final SideStage mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
private final StageTaskUnfoldController mSideUnfoldController;
+ private final DisplayLayout mDisplayLayout;
@SplitPosition
private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -147,8 +152,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private DisplayAreaInfo mDisplayAreaInfo;
private final Context mContext;
private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
+ private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
private final DisplayInsetsController mDisplayInsetsController;
+ private final TransactionPool mTransactionPool;
private final SplitScreenTransitions mSplitTransitions;
private final SplitscreenEventLogger mLogger;
private final Optional<RecentTasksController> mRecentTasks;
@@ -156,28 +163,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// and exit, since exit itself can trigger a number of changes that update the stages.
private boolean mShouldUpdateRecents;
private boolean mExitSplitScreenOnHide;
- private boolean mKeyguardOccluded;
- private boolean mDeviceSleep;
- private boolean mIsDividerRemoteAnimating;
-
- @StageType
- private int mDismissTop = NO_DISMISS;
/** The target stage to dismiss to when unlock after folded. */
@StageType
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
- private final Runnable mOnTransitionAnimationComplete = () -> {
- // If still playing, let it finish.
- if (!isSplitScreenVisible()) {
- // Update divider state after animation so that it is still around and positioned
- // properly for the animation itself.
- setDividerVisibility(false);
- mSplitLayout.resetDividerPosition();
- }
- mDismissTop = NO_DISMISS;
- };
-
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
new SplitWindowManager.ParentContainerCallbacks() {
@Override
@@ -193,6 +183,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, SplitscreenEventLogger logger,
@@ -227,22 +218,27 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSurfaceSession,
iconProvider,
mSideUnfoldController);
+ mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
+ mTransactionPool = transactionPool;
mRootTDAOrganizer.registerListener(displayId, this);
final DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
- mOnTransitionAnimationComplete);
+ this::onTransitionAnimationComplete, this);
+ mDisplayController.addDisplayWindowListener(this);
+ mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId));
transitions.addHandler(this);
}
@VisibleForTesting
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
- MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController,
+ MainStage mainStage, SideStage sideStage, DisplayController displayController,
+ DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger,
@@ -255,16 +251,20 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTaskOrganizer = taskOrganizer;
mMainStage = mainStage;
mSideStage = sideStage;
+ mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
+ mTransactionPool = transactionPool;
mRootTDAOrganizer.registerListener(displayId, this);
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
- mOnTransitionAnimationComplete);
+ this::onTransitionAnimationComplete, this);
mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mLogger = logger;
mRecentTasks = recentTasks;
+ mDisplayController.addDisplayWindowListener(this);
+ mDisplayLayout = new DisplayLayout();
transitions.addHandler(this);
}
@@ -316,7 +316,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!evictWct.isEmpty()) {
wct.merge(evictWct, true /* transfer */);
}
- mTaskOrganizer.applyTransaction(wct);
+
+ if (ENABLE_SHELL_TRANSITIONS) {
+ prepareEnterSplitScreen(wct);
+ mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE,
+ wct, null, this);
+ } else {
+ mTaskOrganizer.applyTransaction(wct);
+ }
return true;
}
@@ -343,12 +350,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
sideOptions = sideOptions != null ? sideOptions : new Bundle();
setSideStagePosition(sidePosition, wct);
+ mSplitLayout.setDivideRatio(splitRatio);
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct);
- mSplitLayout.setDivideRatio(splitRatio);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
addActivityOptions(sideOptions, mSideStage);
@@ -365,8 +372,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
float splitRatio, RemoteAnimationAdapter adapter) {
- // Ensure divider is invisible before transition.
- setDividerVisibility(false /* visible */);
// Init divider first to make divider leash for remote animation target.
mSplitLayout.init();
// Set false to avoid record new bounds with old task still on top;
@@ -384,7 +389,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
- mIsDividerRemoteAnimating = true;
RemoteAnimationTarget[] augmentedNonApps =
new RemoteAnimationTarget[nonApps.length + 1];
for (int i = 0; i < nonApps.length; ++i) {
@@ -396,11 +400,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
- mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
- setDividerVisibility(true /* visible */);
mSyncQueue.queue(evictWct);
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
finishedCallback.onAnimationFinished();
}
};
@@ -421,11 +423,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onAnimationCancelled() {
- mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
- setDividerVisibility(true /* visible */);
mSyncQueue.queue(evictWct);
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
try {
adapter.getRunner().onAnimationCancelled();
} catch (RemoteException e) {
@@ -469,15 +469,114 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTaskOrganizer.applyTransaction(wct);
}
- public void startIntent(PendingIntent intent, Intent fillInIntent,
- @StageType int stage, @SplitPosition int position,
- @androidx.annotation.Nullable Bundle options,
- @Nullable RemoteTransition remoteTransition) {
+ /** Start an intent and a task ordered by {@code intentFirst}. */
+ void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
+ int taskId, boolean intentFirst, @Nullable Bundle mainOptions,
+ @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
+ RemoteAnimationAdapter adapter) {
+ // TODO: try pulling the first chunk of this method into a method so that it can be shared
+ // with startTasksWithLegacyTransition. So far attempts to do so result in failure in split.
+
+ // Init divider first to make divider leash for remote animation target.
+ mSplitLayout.init();
+ // Set false to avoid record new bounds with old task still on top;
+ mShouldUpdateRecents = false;
final WindowContainerTransaction wct = new WindowContainerTransaction();
- options = resolveStartStage(stage, position, options, wct);
- wct.sendPendingIntent(intent, fillInIntent, options);
- mSplitTransitions.startEnterTransition(
- TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this);
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
+ prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
+ // Need to add another wrapper here in shell so that we can inject the divider bar
+ // and also manage the process elevation via setRunningRemote
+ IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ final IRemoteAnimationFinishedCallback finishedCallback) {
+ RemoteAnimationTarget[] augmentedNonApps =
+ new RemoteAnimationTarget[nonApps.length + 1];
+ for (int i = 0; i < nonApps.length; ++i) {
+ augmentedNonApps[i] = nonApps[i];
+ }
+ augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
+
+ IRemoteAnimationFinishedCallback wrapCallback =
+ new IRemoteAnimationFinishedCallback.Stub() {
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ mShouldUpdateRecents = true;
+ mSyncQueue.queue(evictWct);
+ mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
+ finishedCallback.onAnimationFinished();
+ }
+ };
+ try {
+ try {
+ ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
+ adapter.getCallingApplication());
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Unable to boost animation thread. This should only happen"
+ + " during unit tests");
+ }
+ adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
+ augmentedNonApps, wrapCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ mShouldUpdateRecents = true;
+ mSyncQueue.queue(evictWct);
+ mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
+ try {
+ adapter.getRunner().onAnimationCancelled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+ };
+ RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
+ wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
+
+ if (mainOptions == null) {
+ mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
+ } else {
+ ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
+ mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+ mainOptions = mainActivityOptions.toBundle();
+ }
+
+ sideOptions = sideOptions != null ? sideOptions : new Bundle();
+ setSideStagePosition(sidePosition, wct);
+
+ mSplitLayout.setDivideRatio(splitRatio);
+ if (mMainStage.isActive()) {
+ mMainStage.moveToTop(getMainStageBounds(), wct);
+ } else {
+ // Build a request WCT that will launch both apps such that task 0 is on the main stage
+ // while task 1 is on the side stage.
+ mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
+ }
+ mSideStage.moveToTop(getSideStageBounds(), wct);
+
+ // Make sure the launch options will put tasks in the corresponding split roots
+ addActivityOptions(mainOptions, mMainStage);
+ addActivityOptions(sideOptions, mSideStage);
+
+ // Add task launch requests
+ if (intentFirst) {
+ wct.sendPendingIntent(pendingIntent, fillInIntent, mainOptions);
+ wct.startTask(taskId, sideOptions);
+ } else {
+ wct.startTask(taskId, mainOptions);
+ wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
+ }
+
+ // Using legacy transitions, so we can't use blast sync since it conflicts.
+ mTaskOrganizer.applyTransaction(wct);
}
/**
@@ -508,8 +607,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
}
} else {
- // Exit split-screen and launch fullscreen since stage wasn't specified.
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ Slog.w(TAG,
+ "No stage type nor split position specified to resolve start stage");
}
break;
}
@@ -593,29 +692,54 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTaskOrganizer.applyTransaction(wct);
}
- void onKeyguardOccludedChanged(boolean occluded) {
- // Do not exit split directly, because it needs to wait for task info update to determine
- // which task should remain on top after split dismissed.
- mKeyguardOccluded = occluded;
- }
-
void onKeyguardVisibilityChanged(boolean showing) {
- if (!showing && mMainStage.isActive()
- && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) {
- exitSplitScreen(mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
- EXIT_REASON_DEVICE_FOLDED);
+ if (!mMainStage.isActive()) {
+ return;
+ }
+
+ if (ENABLE_SHELL_TRANSITIONS) {
+ // Update divider visibility so it won't float on top of keyguard.
+ setDividerVisibility(!showing, null /* transaction */);
+ }
+
+ if (!showing && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct);
+ mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
+ mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED);
+ } else {
+ exitSplitScreen(
+ mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
+ EXIT_REASON_DEVICE_FOLDED);
+ }
}
}
void onFinishedWakingUp() {
- if (mMainStage.isActive()) {
- exitSplitScreenIfKeyguardOccluded();
+ if (!mMainStage.isActive()) {
+ return;
}
- mDeviceSleep = false;
- }
- void onFinishedGoingToSleep() {
- mDeviceSleep = true;
+ // Check if there's only one stage visible while keyguard occluded.
+ final boolean mainStageVisible = mMainStage.mRootTaskInfo.isVisible;
+ final boolean oneStageVisible =
+ mMainStage.mRootTaskInfo.isVisible != mSideStage.mRootTaskInfo.isVisible;
+ if (oneStageVisible) {
+ // Dismiss split because there's show-when-locked activity showing on top of keyguard.
+ // Also make sure the task contains show-when-locked activity remains on top after split
+ // dismissed.
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ final StageTaskListener toTop = mainStageVisible ? mMainStage : mSideStage;
+ exitSplitScreen(toTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
+ } else {
+ final int dismissTop = mainStageVisible ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(dismissTop, wct);
+ mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
+ dismissTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
+ }
+ }
}
void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
@@ -646,19 +770,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
applyExitSplitScreen(childrenToTop, wct, exitReason);
}
- private void exitSplitScreenIfKeyguardOccluded() {
- final boolean mainStageVisible = mMainStageListener.mVisible;
- final boolean oneStageVisible = mainStageVisible ^ mSideStageListener.mVisible;
- if (mDeviceSleep && mKeyguardOccluded && oneStageVisible) {
- // Only the stages include show-when-locked activity is visible while keyguard occluded.
- // Dismiss split because there's show-when-locked activity showing on top of keyguard.
- // Also make sure the task contains show-when-locked activity remains on top after split
- // dismissed.
- final StageTaskListener toTop = mainStageVisible ? mMainStage : mSideStage;
- exitSplitScreen(toTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
- }
- }
-
private void applyExitSplitScreen(StageTaskListener childrenToTop,
WindowContainerTransaction wct, @ExitReason int exitReason) {
mRecentTasks.ifPresent(recentTasks -> {
@@ -683,8 +794,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
.setWindowCrop(mSideStage.mRootLeash, null));
// Hide divider and reset its position.
- setDividerVisibility(false);
mSplitLayout.resetDividerPosition();
+ mSplitLayout.release();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
// Log the exit
@@ -708,6 +819,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
case EXIT_REASON_APP_FINISHED:
// One of the children enters PiP
case EXIT_REASON_CHILD_TASK_ENTER_PIP:
+ // One of the apps occludes lock screen.
+ case EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP:
+ // User has unlocked the device after folded
+ case EXIT_REASON_DEVICE_FOLDED:
return true;
default:
return false;
@@ -719,12 +834,47 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
* an existing WindowContainerTransaction (rather than applying immediately). This is intended
* to be used when exiting split might be bundled with other window operations.
*/
- void prepareExitSplitScreen(@StageType int stageToTop,
+ private void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
+ if (!mMainStage.isActive()) return;
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
}
+ private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
+ prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED);
+ }
+
+ /**
+ * Prepare transaction to active split screen. If there's a task indicated, the task will be put
+ * into side stage.
+ */
+ void prepareEnterSplitScreen(WindowContainerTransaction wct,
+ @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) {
+ if (mMainStage.isActive()) return;
+
+ if (taskInfo != null) {
+ setSideStagePosition(startPosition, wct);
+ mSideStage.addTask(taskInfo, wct);
+ }
+ mMainStage.activate(getMainStageBounds(), wct, true /* includingTopTask */);
+ mSideStage.moveToTop(getSideStageBounds(), wct);
+ }
+
+ void finishEnterSplitScreen(SurfaceControl.Transaction t) {
+ mSplitLayout.init();
+ setDividerVisibility(true, t);
+ setSplitsVisible(true);
+ mShouldUpdateRecents = true;
+ updateRecentTasksSplitPair();
+ if (!mLogger.hasStartedSession()) {
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
+ }
+ }
+
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
outTopOrLeftBounds.set(mSplitLayout.getBounds1());
outBottomOrRightBounds.set(mSplitLayout.getBounds2());
@@ -841,6 +991,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mMainUnfoldController != null && mSideUnfoldController != null) {
mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible);
mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible);
+ updateUnfoldBounds();
}
}
@@ -865,67 +1016,53 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- private void setDividerVisibility(boolean visible) {
- if (mIsDividerRemoteAnimating || mDividerVisible == visible) return;
- mDividerVisible = visible;
- if (visible) {
- mSplitLayout.init();
- updateUnfoldBounds();
- } else {
- mSplitLayout.release();
- }
- sendSplitVisibilityChanged();
- }
-
private void onStageVisibilityChanged(StageListenerImpl stageListener) {
final boolean sideStageVisible = mSideStageListener.mVisible;
final boolean mainStageVisible = mMainStageListener.mVisible;
- final boolean bothStageVisible = sideStageVisible && mainStageVisible;
- final boolean bothStageInvisible = !sideStageVisible && !mainStageVisible;
- final boolean sameVisibility = sideStageVisible == mainStageVisible;
- // Only add or remove divider when both visible or both invisible to avoid sometimes we only
- // got one stage visibility changed for a moment and it will cause flicker.
- if (sameVisibility) {
- setDividerVisibility(bothStageVisible);
+
+ // Wait for both stages having the same visibility to prevent causing flicker.
+ if (mainStageVisible != sideStageVisible) {
+ return;
}
- if (bothStageInvisible) {
+ if (!mainStageVisible) {
+ // Both stages are not visible, check if it needs to dismiss split screen.
if (mExitSplitScreenOnHide
- // Don't dismiss staged split when both stages are not visible due to sleeping
- // display, like the cases keyguard showing or screen off.
+ // Don't dismiss split screen when both stages are not visible due to sleeping
+ // display.
|| (!mMainStage.mRootTaskInfo.isSleeping
&& !mSideStage.mRootTaskInfo.isSleeping)) {
- // Don't dismiss staged split when both stages are not visible due to sleeping display,
- // like the cases keyguard showing or screen off.
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RETURN_HOME);
}
}
- exitSplitScreenIfKeyguardOccluded();
mSyncQueue.runInSync(t -> {
- // Same above, we only set root tasks and divider leash visibility when both stage
- // change to visible or invisible to avoid flicker.
- if (sameVisibility) {
- t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
- .setVisibility(mMainStage.mRootLeash, bothStageVisible);
- applyDividerVisibility(t);
- }
+ t.setVisibility(mSideStage.mRootLeash, sideStageVisible)
+ .setVisibility(mMainStage.mRootLeash, mainStageVisible);
+ setDividerVisibility(mainStageVisible, t);
});
}
- private void applyDividerVisibility(SurfaceControl.Transaction t) {
- if (mIsDividerRemoteAnimating) return;
+ private void setDividerVisibility(boolean visible, @Nullable SurfaceControl.Transaction t) {
+ mDividerVisible = visible;
+ sendSplitVisibilityChanged();
+ if (t != null) {
+ applyDividerVisibility(t);
+ } else {
+ mSyncQueue.runInSync(transaction -> applyDividerVisibility(transaction));
+ }
+ }
+ private void applyDividerVisibility(SurfaceControl.Transaction t) {
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
if (dividerLeash == null) return;
if (mDividerVisible) {
- t.show(dividerLeash)
- .setAlpha(dividerLeash, 1)
- .setLayer(dividerLeash, SPLIT_DIVIDER_LAYER)
- .setPosition(dividerLeash,
- mSplitLayout.getDividerBounds().left,
- mSplitLayout.getDividerBounds().top);
+ t.show(dividerLeash);
+ t.setAlpha(dividerLeash, 1);
+ t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER);
+ t.setPosition(dividerLeash,
+ mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top);
} else {
t.hide(dividerLeash);
}
@@ -944,11 +1081,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
} else if (isSideStage) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mSplitLayout.init();
// Make sure the main stage is active.
- mMainStage.activate(getMainStageBounds(), wct, true /* reparent */);
- mSideStage.moveToTop(getSideStageBounds(), wct);
+ prepareEnterSplitScreen(wct);
mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
+ mSyncQueue.runInSync(t -> {
+ updateSurfaceBounds(mSplitLayout, t);
+ setDividerVisibility(true, t);
+ });
}
if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
mShouldUpdateRecents = true;
@@ -963,23 +1103,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- @VisibleForTesting
- IBinder onSnappedToDismissTransition(boolean mainStageToTop) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareExitSplitScreen(mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE, wct);
- return mSplitTransitions.startSnapToDismiss(wct, this);
- }
-
@Override
public void onSnappedToDismiss(boolean bottomOrRight) {
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
- if (ENABLE_SHELL_TRANSITIONS) {
- onSnappedToDismissTransition(mainStageToTop);
+
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
return;
}
- exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
+
+ final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(dismissTop, wct);
+ mSplitTransitions.startDismissTransition(
+ null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
}
@Override
@@ -1020,11 +1159,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void updateUnfoldBounds() {
if (mMainUnfoldController != null && mSideUnfoldController != null) {
- mMainUnfoldController.onLayoutChanged(getMainStageBounds());
- mSideUnfoldController.onLayoutChanged(getSideStageBounds());
+ mMainUnfoldController.onLayoutChanged(getMainStageBounds(), getMainStagePosition(),
+ isLandscape());
+ mSideUnfoldController.onLayoutChanged(getSideStageBounds(), getSideStagePosition(),
+ isLandscape());
}
}
+ private boolean isLandscape() {
+ return mSplitLayout.isLandscape();
+ }
+
/**
* Populates `wct` with operations that match the split windows to the current layout.
* To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied
@@ -1100,10 +1245,48 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
&& mMainStage.isActive()) {
+ // TODO(b/204925795): With Shell transition, We are handle roation case for apply split
+ // bounds at onRotateDisplay. But still need to handle unfold case.
+ if (ENABLE_SHELL_TRANSITIONS) {
+ updateUnfoldBounds();
+ return;
+ }
onLayoutSizeChanged(mSplitLayout);
}
}
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mDisplayController.addDisplayChangingController(this::onRotateDisplay);
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId));
+ }
+
+ private void onRotateDisplay(int displayId, int fromRotation, int toRotation,
+ WindowContainerTransaction wct) {
+ if (!mMainStage.isActive()) return;
+ // Only do this when shell transition
+ if (!ENABLE_SHELL_TRANSITIONS) return;
+
+ final SurfaceControl.Transaction t = mTransactionPool.acquire();
+ setDividerVisibility(false, t);
+ mDisplayLayout.rotateTo(mContext.getResources(), toRotation);
+ mSplitLayout.rotateTo(toRotation, mDisplayLayout.stableInsets());
+ updateWindowBounds(mSplitLayout, wct);
+ updateUnfoldBounds();
+ t.apply();
+ mTransactionPool.release(t);
+ }
+
private void onFoldedStateChanged(boolean folded) {
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
if (!folded) return;
@@ -1152,14 +1335,25 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Nullable TransitionRequestInfo request) {
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
- // still want to monitor everything while in split-screen, so return non-null.
- return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
+ // Still want to monitor everything while in split-screen, so return non-null.
+ return mMainStage.isActive() ? new WindowContainerTransaction() : null;
+ } else if (triggerTask.displayId != mDisplayId) {
+ // Skip handling task on the other display.
+ return null;
}
WindowContainerTransaction out = null;
final @WindowManager.TransitionType int type = request.getType();
- if (isSplitScreenVisible()) {
- // try to handle everything while in split-screen, so return a WCT even if it's empty.
+ final boolean isOpening = isOpeningType(type);
+ final boolean inFullscreen = triggerTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+
+ if (isOpening && inFullscreen) {
+ // One task is opening into fullscreen mode, remove the corresponding split record.
+ mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
+ }
+
+ if (mMainStage.isActive()) {
+ // Try to handle everything while in split-screen, so return a WCT even if it's empty.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
+ " sideChildren=%d", triggerTask.taskId, transitTypeToString(type),
@@ -1167,50 +1361,73 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
out = new WindowContainerTransaction();
final StageTaskListener stage = getStageOfTask(triggerTask);
if (stage != null) {
- // dismiss split if the last task in one of the stages is going away
+ // Dismiss split if the last task in one of the stages is going away
if (isClosingType(type) && stage.getChildCount() == 1) {
// The top should be the opposite side that is closing:
- mDismissTop = getStageType(stage) == STAGE_TYPE_MAIN
- ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+ int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
+ : STAGE_TYPE_MAIN;
+ prepareExitSplitScreen(dismissTop, out);
+ mSplitTransitions.startDismissTransition(transition, out, this, dismissTop,
+ EXIT_REASON_APP_FINISHED);
}
- } else {
- if (triggerTask.getActivityType() == ACTIVITY_TYPE_HOME && isOpeningType(type)) {
- // Going home so dismiss both.
- mDismissTop = STAGE_TYPE_UNDEFINED;
+ } else if (isOpening && inFullscreen) {
+ final int activityType = triggerTask.getActivityType();
+ if (activityType == ACTIVITY_TYPE_ASSISTANT) {
+ // We don't want assistant panel to dismiss split screen, so do nothing.
+ } else if (activityType == ACTIVITY_TYPE_HOME
+ || activityType == ACTIVITY_TYPE_RECENTS) {
+ // Enter overview panel, so start recent transition.
+ mSplitTransitions.startRecentTransition(transition, out, this,
+ request.getRemoteTransition());
+ } else {
+ // Occluded by the other fullscreen task, so dismiss both.
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
+ mSplitTransitions.startDismissTransition(transition, out, this,
+ STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
}
}
- if (mDismissTop != NO_DISMISS) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
- + " deduced Dismiss from request. toTop=%s",
- stageTypeToString(mDismissTop));
- prepareExitSplitScreen(mDismissTop, out);
- mSplitTransitions.mPendingDismiss = transition;
- }
} else {
- // Not in split mode, so look for an open into a split stage just so we can whine and
- // complain about how this isn't a supported operation.
- if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) {
- if (getStageOfTask(triggerTask) != null) {
- throw new IllegalStateException("Entering split implicitly with only one task"
- + " isn't supported.");
- }
+ if (isOpening && getStageOfTask(triggerTask) != null) {
+ // One task is appearing into split, prepare to enter split screen.
+ out = new WindowContainerTransaction();
+ prepareEnterSplitScreen(out);
+ mSplitTransitions.mPendingEnter = transition;
}
}
return out;
}
@Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ Transitions.TransitionFinishCallback finishCallback) {
+ mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ }
+
+ @Override
+ public void onTransitionMerged(@NonNull IBinder transition) {
+ // Once the pending enter transition got merged, make sure to bring divider bar visible and
+ // clear the pending transition from cache to prevent mess-up the following state.
+ if (transition == mSplitTransitions.mPendingEnter) {
+ finishEnterSplitScreen(null);
+ mSplitTransitions.mPendingEnter = null;
+ }
+ }
+
+ @Override
public boolean startAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (transition != mSplitTransitions.mPendingDismiss
- && transition != mSplitTransitions.mPendingEnter) {
+ if (transition != mSplitTransitions.mPendingEnter
+ && transition != mSplitTransitions.mPendingRecent
+ && (mSplitTransitions.mPendingDismiss == null
+ || mSplitTransitions.mPendingDismiss.mTransition != transition)) {
// Not entering or exiting, so just do some house-keeping and validation.
// If we're not in split-mode, just abort so something else can handle it.
- if (!isSplitScreenVisible()) return false;
+ if (!mMainStage.isActive()) return false;
for (int iC = 0; iC < info.getChanges().size(); ++iC) {
final TransitionInfo.Change change = info.getChanges().get(iC);
@@ -1228,6 +1445,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
Log.w(TAG, "Expected onTaskVanished on " + stage + " to have been called"
+ " with " + taskInfo.taskId + " before startAnimation().");
}
+ } else if (info.getType() == TRANSIT_CHANGE
+ && change.getStartRotation() != change.getEndRotation()) {
+ // Show the divider after transition finished.
+ setDividerVisibility(true, finishTransaction);
}
}
if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
@@ -1249,8 +1470,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
boolean shouldAnimate = true;
if (mSplitTransitions.mPendingEnter == transition) {
shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
- } else if (mSplitTransitions.mPendingDismiss == transition) {
- shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
+ } else if (mSplitTransitions.mPendingRecent == transition) {
+ shouldAnimate = startPendingRecentAnimation(transition, info, startTransaction);
+ } else if (mSplitTransitions.mPendingDismiss != null
+ && mSplitTransitions.mPendingDismiss.mTransition == transition) {
+ shouldAnimate = startPendingDismissAnimation(
+ mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
}
if (!shouldAnimate) return false;
@@ -1259,63 +1484,74 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
+ void onTransitionAnimationComplete() {
+ // If still playing, let it finish.
+ if (!mMainStage.isActive()) {
+ // Update divider state after animation so that it is still around and positioned
+ // properly for the animation itself.
+ mSplitLayout.release();
+ mSplitLayout.resetDividerPosition();
+ mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ }
+ }
+
private boolean startPendingEnterAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
- if (info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN) {
- // First, verify that we actually have opened 2 apps in split.
- TransitionInfo.Change mainChild = null;
- TransitionInfo.Change sideChild = null;
- for (int iC = 0; iC < info.getChanges().size(); ++iC) {
- final TransitionInfo.Change change = info.getChanges().get(iC);
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo == null || !taskInfo.hasParentTask()) continue;
- final @StageType int stageType = getStageType(getStageOfTask(taskInfo));
- if (stageType == STAGE_TYPE_MAIN) {
- mainChild = change;
- } else if (stageType == STAGE_TYPE_SIDE) {
- sideChild = change;
- }
+ // First, verify that we actually have opened apps in both splits.
+ TransitionInfo.Change mainChild = null;
+ TransitionInfo.Change sideChild = null;
+ for (int iC = 0; iC < info.getChanges().size(); ++iC) {
+ final TransitionInfo.Change change = info.getChanges().get(iC);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo == null || !taskInfo.hasParentTask()) continue;
+ final @StageType int stageType = getStageType(getStageOfTask(taskInfo));
+ if (stageType == STAGE_TYPE_MAIN) {
+ mainChild = change;
+ } else if (stageType == STAGE_TYPE_SIDE) {
+ sideChild = change;
}
+ }
+
+ // TODO: fallback logic. Probably start a new transition to exit split before applying
+ // anything here. Ideally consolidate with transition-merging.
+ if (info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
+ if (mainChild == null && sideChild == null) {
+ throw new IllegalStateException("Launched a task in split, but didn't receive any"
+ + " task in transition.");
+ }
+ } else {
if (mainChild == null || sideChild == null) {
throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
+ " 2 tasks in transition. Possibly one of them failed to launch");
- // TODO: fallback logic. Probably start a new transition to exit split before
- // applying anything here. Ideally consolidate with transition-merging.
}
+ }
- // Update local states (before animating).
- setDividerVisibility(true);
- setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */,
- null /* wct */);
- setSplitsVisible(true);
-
- addDividerBarToTransition(info, t, true /* show */);
-
- // Make some noise if things aren't totally expected. These states shouldn't effect
- // transitions locally, but remotes (like Launcher) may get confused if they were
- // depending on listener callbacks. This can happen because task-organizer callbacks
- // aren't serialized with transition callbacks.
- // TODO(b/184679596): Find a way to either include task-org information in
- // the transition, or synchronize task-org callbacks.
- if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
- Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
- + " to have been called with " + mainChild.getTaskInfo().taskId
- + " before startAnimation().");
- }
- if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
- Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
- + " to have been called with " + sideChild.getTaskInfo().taskId
- + " before startAnimation().");
- }
- return true;
- } else {
- // TODO: other entry method animations
- throw new RuntimeException("Unsupported split-entry");
+ // Make some noise if things aren't totally expected. These states shouldn't effect
+ // transitions locally, but remotes (like Launcher) may get confused if they were
+ // depending on listener callbacks. This can happen because task-organizer callbacks
+ // aren't serialized with transition callbacks.
+ // TODO(b/184679596): Find a way to either include task-org information in
+ // the transition, or synchronize task-org callbacks.
+ if (mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
+ Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
+ + " to have been called with " + mainChild.getTaskInfo().taskId
+ + " before startAnimation().");
+ }
+ if (sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
+ Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
+ + " to have been called with " + sideChild.getTaskInfo().taskId
+ + " before startAnimation().");
}
+
+ finishEnterSplitScreen(t);
+ addDividerBarToTransition(info, t, true /* show */);
+ return true;
}
- private boolean startPendingDismissAnimation(@NonNull IBinder transition,
- @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
+ private boolean startPendingDismissAnimation(
+ @NonNull SplitScreenTransitions.DismissTransition dismissTransition,
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction finishT) {
// Make some noise if things aren't totally expected. These states shouldn't effect
// transitions locally, but remotes (like Launcher) may get confused if they were
// depending on listener callbacks. This can happen because task-organizer callbacks
@@ -1343,34 +1579,80 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
+ "] before startAnimation().");
}
+ mRecentTasks.ifPresent(recentTasks -> {
+ // Notify recents if we are exiting in a way that breaks the pair, and disable further
+ // updates to splits in the recents until we enter split again
+ if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ recentTasks.removeSplitPair(taskInfo.taskId);
+ }
+ }
+ }
+ });
+ mShouldUpdateRecents = false;
+
// Update local states.
setSplitsVisible(false);
// Wait until after animation to update divider
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Reset crops so they don't interfere with subsequent launches
- t.setWindowCrop(mMainStage.mRootLeash, null);
- t.setWindowCrop(mSideStage.mRootLeash, null);
- }
-
- if (mDismissTop == STAGE_TYPE_UNDEFINED) {
- // Going home (dismissing both splits)
+ // Reset crops so they don't interfere with subsequent launches
+ t.setWindowCrop(mMainStage.mRootLeash, null);
+ t.setWindowCrop(mSideStage.mRootLeash, null);
+ if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
+ logExit(dismissTransition.mReason);
// TODO: Have a proper remote for this. Until then, though, reset state and use the
// normal animation stuff (which falls back to the normal launcher remote).
- t.hide(mSplitLayout.getDividerLeash());
- setDividerVisibility(false);
+ setDividerVisibility(false, t);
+ mSplitLayout.release();
mSplitTransitions.mPendingDismiss = null;
return false;
+ } else {
+ logExitToStage(dismissTransition.mReason,
+ dismissTransition.mDismissTop == STAGE_TYPE_MAIN);
}
addDividerBarToTransition(info, t, false /* show */);
// We're dismissing split by moving the other one to fullscreen.
// Since we don't have any animations for this yet, just use the internal example
// animations.
+
+ // Hide divider and dim layer on transition finished.
+ setDividerVisibility(false, finishT);
+ finishT.hide(mMainStage.mDimLayer);
+ finishT.hide(mSideStage.mDimLayer);
return true;
}
+ private boolean startPendingRecentAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
+ setDividerVisibility(false, t);
+ return true;
+ }
+
+ void finishRecentAnimation(boolean dismissSplit) {
+ // Exclude the case that the split screen has been dismissed already.
+ if (!mMainStage.isActive()) {
+ // The latest split dismissing transition might be a no-op transition and thus won't
+ // callback startAnimation, update split visibility here to cover this kind of no-op
+ // transition case.
+ setSplitsVisible(false);
+ return;
+ }
+
+ if (dismissSplit) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
+ STAGE_TYPE_UNDEFINED, EXIT_REASON_RETURN_HOME);
+ } else {
+ setDividerVisibility(true, null /* t */);
+ }
+ }
+
private void addDividerBarToTransition(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, boolean show) {
final SurfaceControl leash = mSplitLayout.getDividerLeash();
@@ -1505,7 +1787,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onNoLongerSupportMultiWindow() {
if (mMainStage.isActive()) {
- StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+ }
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ mSplitTransitions.startDismissTransition(null /* transition */, wct,
+ StageCoordinator.this, STAGE_TYPE_UNDEFINED,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 04e20db369ef..83534c178469 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -302,6 +302,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
+ // Clear overridden bounds and windowing mode to make sure the child task can inherit
+ // windowing mode and bounds from split root.
+ wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
+ .setBounds(task.token, null);
+
wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
index 4849163e96fd..59eecb5db136 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
@@ -18,11 +18,15 @@ package com.android.wm.shell.splitscreen;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+
import android.animation.RectEvaluator;
import android.animation.TypeEvaluator;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.util.SparseArray;
import android.view.InsetsSource;
@@ -33,6 +37,7 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
import com.android.wm.shell.unfold.UnfoldBackgroundController;
@@ -166,12 +171,13 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
* Called when split screen stage bounds changed
* @param bounds new bounds for this stage
*/
- public void onLayoutChanged(Rect bounds) {
+ public void onLayoutChanged(Rect bounds, @SplitPosition int splitPosition,
+ boolean isLandscape) {
mStageBounds.set(bounds);
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
- context.update();
+ context.update(splitPosition, isLandscape);
}
}
@@ -200,20 +206,27 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
final Rect mEndCropRect = new Rect();
final Rect mCurrentCropRect = new Rect();
+ private @SplitPosition int mSplitPosition = SPLIT_POSITION_UNDEFINED;
+ private boolean mIsLandscape = false;
+
private AnimationContext(SurfaceControl leash) {
this.mLeash = leash;
update();
}
+ private void update(@SplitPosition int splitPosition, boolean isLandscape) {
+ this.mSplitPosition = splitPosition;
+ this.mIsLandscape = isLandscape;
+ update();
+ }
+
private void update() {
mStartCropRect.set(mStageBounds);
- if (mTaskbarInsetsSource != null) {
+ boolean taskbarExpanded = isTaskbarExpanded();
+ if (taskbarExpanded) {
// Only insets the cropping window with taskbar when taskbar is expanded
- if (mTaskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
- mStartCropRect.inset(mTaskbarInsetsSource
- .calculateVisibleInsets(mStartCropRect));
- }
+ mStartCropRect.inset(mTaskbarInsetsSource.calculateVisibleInsets(mStartCropRect));
}
// Offset to surface coordinates as layout bounds are in screen coordinates
@@ -223,7 +236,46 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
int maxSize = Math.max(mEndCropRect.width(), mEndCropRect.height());
int margin = (int) (maxSize * CROPPING_START_MARGIN_FRACTION);
- mStartCropRect.inset(margin, margin, margin, margin);
+
+ // Sides adjacent to split bar or task bar are not be animated.
+ Insets margins;
+ if (mIsLandscape) { // Left and right splits.
+ margins = getLandscapeMargins(margin, taskbarExpanded);
+ } else { // Top and bottom splits.
+ margins = getPortraitMargins(margin, taskbarExpanded);
+ }
+ mStartCropRect.inset(margins);
+ }
+
+ private Insets getLandscapeMargins(int margin, boolean taskbarExpanded) {
+ int left = margin;
+ int right = margin;
+ int bottom = taskbarExpanded ? 0 : margin; // Taskbar margin.
+ if (mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ right = 0; // Divider margin.
+ } else {
+ left = 0; // Divider margin.
+ }
+ return Insets.of(left, /* top= */ margin, right, bottom);
+ }
+
+ private Insets getPortraitMargins(int margin, boolean taskbarExpanded) {
+ int bottom = margin;
+ int top = margin;
+ if (mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ bottom = 0; // Divider margin.
+ } else { // Bottom split.
+ top = 0; // Divider margin.
+ if (taskbarExpanded) {
+ bottom = 0; // Taskbar margin.
+ }
+ }
+ return Insets.of(/* left= */ margin, top, /* right= */ margin, bottom);
+ }
+
+ private boolean isTaskbarExpanded() {
+ return mTaskbarInsetsSource != null
+ && mTaskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
index af9a5aa501e8..018365420177 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
@@ -127,12 +127,6 @@ class SplitScreenTransitions {
}
// TODO(shell-transitions): screenshot here
final Rect startBounds = new Rect(change.getStartAbsBounds());
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Dismissing split via snap which means the still-visible task has been
- // dragged to its end position at animation start so reflect that here.
- startBounds.offsetTo(change.getEndAbsBounds().left,
- change.getEndAbsBounds().top);
- }
final Rect endBounds = new Rect(change.getEndAbsBounds());
startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index e7b5744dd21b..014f02bcf8b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -56,7 +56,7 @@ import com.android.wm.shell.common.TransactionPool;
public class SplashScreenExitAnimation implements Animator.AnimatorListener {
private static final boolean DEBUG_EXIT_ANIMATION = false;
private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
- private static final String TAG = StartingSurfaceDrawer.TAG;
+ private static final String TAG = StartingWindowController.TAG;
private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f);
private static final Interpolator MASK_RADIUS_INTERPOLATOR =
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 b191cabcf6aa..e255e44503fb 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
@@ -54,6 +54,7 @@ import android.view.ContextThemeWrapper;
import android.view.SurfaceControl;
import android.view.View;
import android.window.SplashScreenView;
+import android.window.StartingWindowInfo;
import android.window.StartingWindowInfo.StartingWindowType;
import com.android.internal.R;
@@ -61,9 +62,11 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.palette.Palette;
import com.android.internal.graphics.palette.Quantizer;
import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.List;
import java.util.function.Consumer;
@@ -78,8 +81,7 @@ import java.util.function.UnaryOperator;
* @hide
*/
public class SplashscreenContentDrawer {
- private static final String TAG = StartingSurfaceDrawer.TAG;
- private static final boolean DEBUG = StartingSurfaceDrawer.DEBUG_SPLASH_SCREEN;
+ private static final String TAG = StartingWindowController.TAG;
// The acceptable area ratio of foreground_icon_area/background_icon_area, if there is an
// icon which it's non-transparent foreground area is similar to it's background area, then
@@ -137,8 +139,8 @@ public class SplashscreenContentDrawer {
* executed on splash screen thread. Note that the view can be
* null if failed.
*/
- void createContentView(Context context, @StartingWindowType int suggestType, ActivityInfo info,
- int taskId, Consumer<SplashScreenView> splashScreenViewConsumer,
+ void createContentView(Context context, @StartingWindowType int suggestType,
+ StartingWindowInfo info, Consumer<SplashScreenView> splashScreenViewConsumer,
Consumer<Runnable> uiThreadInitConsumer) {
mSplashscreenWorkerHandler.post(() -> {
SplashScreenView contentView;
@@ -149,7 +151,7 @@ public class SplashscreenContentDrawer {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} catch (RuntimeException e) {
Slog.w(TAG, "failed creating starting window content at taskId: "
- + taskId, e);
+ + info.taskInfo.taskId, e);
contentView = null;
}
splashScreenViewConsumer.accept(contentView);
@@ -240,7 +242,7 @@ public class SplashscreenContentDrawer {
return null;
}
- private SplashScreenView makeSplashScreenContentView(Context context, ActivityInfo ai,
+ private SplashScreenView makeSplashScreenContentView(Context context, StartingWindowInfo info,
@StartingWindowType int suggestType, Consumer<Runnable> uiThreadInitConsumer) {
updateDensity();
@@ -249,6 +251,9 @@ public class SplashscreenContentDrawer {
final Drawable legacyDrawable = suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
? peekLegacySplashscreenContent(context, mTmpAttrs) : null;
+ final ActivityInfo ai = info.targetActivityInfo != null
+ ? info.targetActivityInfo
+ : info.taskInfo.topActivityInfo;
final int themeBGColor = legacyDrawable != null
? getBGColorFromCache(ai, () -> estimateWindowBGColor(legacyDrawable))
: getBGColorFromCache(ai, () -> peekWindowBGColor(context, mTmpAttrs));
@@ -257,6 +262,7 @@ public class SplashscreenContentDrawer {
.overlayDrawable(legacyDrawable)
.chooseStyle(suggestType)
.setUiThreadInitConsumer(uiThreadInitConsumer)
+ .setAllowHandleEmpty(info.allowHandleEmptySplashScreen())
.build();
}
@@ -295,12 +301,10 @@ public class SplashscreenContentDrawer {
R.styleable.Window_windowSplashScreenIconBackgroundColor, def),
Color.TRANSPARENT);
typedArray.recycle();
- if (DEBUG) {
- Slog.d(TAG, "window attributes color: "
- + Integer.toHexString(attrs.mWindowBgColor)
- + " icon " + attrs.mSplashScreenIcon + " duration " + attrs.mAnimationDuration
- + " brandImage " + attrs.mBrandingImage);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "getWindowAttrs: window attributes color: %s, replace icon: %b, avd duration: %d",
+ Integer.toHexString(attrs.mWindowBgColor), attrs.mSplashScreenIcon != null,
+ attrs.mAnimationDuration);
}
/** Creates the wrapper with system theme to avoid unexpected styles from app. */
@@ -328,6 +332,7 @@ public class SplashscreenContentDrawer {
private Drawable[] mFinalIconDrawables;
private int mFinalIconSize = mIconSize;
private Consumer<Runnable> mUiThreadInitTask;
+ private boolean mAllowHandleEmpty;
StartingWindowViewBuilder(@NonNull Context context, @NonNull ActivityInfo aInfo) {
mContext = context;
@@ -354,6 +359,11 @@ public class SplashscreenContentDrawer {
return this;
}
+ StartingWindowViewBuilder setAllowHandleEmpty(boolean allowHandleEmpty) {
+ mAllowHandleEmpty = allowHandleEmpty;
+ return this;
+ }
+
SplashScreenView build() {
Drawable iconDrawable;
final int animationDuration;
@@ -385,15 +395,13 @@ public class SplashscreenContentDrawer {
iconDrawable = mContext.getPackageManager().getDefaultActivityIcon();
}
if (!processAdaptiveIcon(iconDrawable)) {
- if (DEBUG) {
- Slog.d(TAG, "The icon is not an AdaptiveIconDrawable");
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "The icon is not an AdaptiveIconDrawable");
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "legacy_icon_factory");
final ShapeIconFactory factory = new ShapeIconFactory(
SplashscreenContentDrawer.this.mContext,
scaledIconDpi, mFinalIconSize);
- final Bitmap bitmap = factory.createScaledBitmapWithoutShadow(
- iconDrawable, true /* shrinkNonAdaptiveIcons */);
+ final Bitmap bitmap = factory.createScaledBitmapWithoutShadow(iconDrawable);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
createIconDrawable(new BitmapDrawable(bitmap), true);
}
@@ -435,14 +443,14 @@ public class SplashscreenContentDrawer {
() -> new DrawableColorTester(iconForeground,
DrawableColorTester.TRANSLUCENT_FILTER /* filterType */),
() -> new DrawableColorTester(adaptiveIconDrawable.getBackground()));
-
- if (DEBUG) {
- Slog.d(TAG, "FgMainColor=" + Integer.toHexString(iconColor.mFgColor)
- + " BgMainColor=" + Integer.toHexString(iconColor.mBgColor)
- + " IsBgComplex=" + iconColor.mIsBgComplex
- + " FromCache=" + (iconColor.mReuseCount > 0)
- + " ThemeColor=" + Integer.toHexString(mThemeColor));
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "processAdaptiveIcon: FgMainColor=%s, BgMainColor=%s, "
+ + "IsBgComplex=%b, FromCache=%b, ThemeColor=%s",
+ Integer.toHexString(iconColor.mFgColor),
+ Integer.toHexString(iconColor.mBgColor),
+ iconColor.mIsBgComplex,
+ iconColor.mReuseCount > 0,
+ Integer.toHexString(mThemeColor));
// Only draw the foreground of AdaptiveIcon to the splash screen if below condition
// meet:
@@ -456,9 +464,8 @@ public class SplashscreenContentDrawer {
&& (isRgbSimilarInHsv(mThemeColor, iconColor.mBgColor)
|| (iconColor.mIsBgGrayscale
&& !isRgbSimilarInHsv(mThemeColor, iconColor.mFgColor)))) {
- if (DEBUG) {
- Slog.d(TAG, "makeSplashScreenContentView: choose fg icon");
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "processAdaptiveIcon: choose fg icon");
// Reference AdaptiveIcon description, outer is 108 and inner is 72, so we
// scale by 192/160 if we only draw adaptiveIcon's foreground.
final float noBgScale =
@@ -469,9 +476,8 @@ public class SplashscreenContentDrawer {
mFinalIconSize = (int) (0.5f + mIconSize * noBgScale);
createIconDrawable(iconForeground, false);
} else {
- if (DEBUG) {
- Slog.d(TAG, "makeSplashScreenContentView: draw whole icon");
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "processAdaptiveIcon: draw whole icon");
createIconDrawable(iconDrawable, false);
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -496,7 +502,8 @@ public class SplashscreenContentDrawer {
.setIconBackground(background)
.setCenterViewDrawable(foreground)
.setAnimationDurationMillis(animationDuration)
- .setUiThreadInitConsumer(uiThreadInitTask);
+ .setUiThreadInitConsumer(uiThreadInitTask)
+ .setAllowHandleEmpty(mAllowHandleEmpty);
if (mSuggestType == STARTING_WINDOW_TYPE_SPLASH_SCREEN
&& mTmpAttrs.mBrandingImage != null) {
@@ -504,9 +511,6 @@ public class SplashscreenContentDrawer {
mBrandingImageHeight);
}
final SplashScreenView splashScreenView = builder.build();
- if (DEBUG) {
- Slog.d(TAG, "fillViewWithIcon surfaceWindowView " + splashScreenView);
- }
if (mSuggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
splashScreenView.addOnAttachStateChangeListener(
new View.OnAttachStateChangeListener() {
@@ -536,10 +540,9 @@ public class SplashscreenContentDrawer {
final float lumB = Color.luminance(b);
final float contrastRatio = lumA > lumB
? (lumA + 0.05f) / (lumB + 0.05f) : (lumB + 0.05f) / (lumA + 0.05f);
- if (DEBUG) {
- Slog.d(TAG, "isRgbSimilarInHsv a: " + Integer.toHexString(a)
- + " b " + Integer.toHexString(b) + " contrast ratio: " + contrastRatio);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "isRgbSimilarInHsv a:%s, b:%s, contrast ratio:%f",
+ Integer.toHexString(a), Integer.toHexString(b), contrastRatio);
if (contrastRatio < 2) {
return true;
}
@@ -560,14 +563,11 @@ public class SplashscreenContentDrawer {
final double square = squareH + squareS + squareV;
final double mean = square / 3;
final double root = Math.sqrt(mean);
- if (DEBUG) {
- Slog.d(TAG, "hsvDiff " + minAngle
- + " ah " + aHsv[0] + " bh " + bHsv[0]
- + " as " + aHsv[1] + " bs " + bHsv[1]
- + " av " + aHsv[2] + " bv " + bHsv[2]
- + " sqH " + squareH + " sqS " + squareS + " sqV " + squareV
- + " root " + root);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "isRgbSimilarInHsv hsvDiff: %d, ah: %f, bh: %f, as: %f, bs: %f, av: %f, bv: %f, "
+ + "sqH: %f, sqS: %f, sqV: %f, rsm: %f",
+ minAngle, aHsv[0], bHsv[0], aHsv[1], bHsv[1], aHsv[2], bHsv[2],
+ squareH, squareS, squareV, root);
return root < 0.1;
}
@@ -598,9 +598,8 @@ public class SplashscreenContentDrawer {
if (drawable instanceof LayerDrawable) {
LayerDrawable layerDrawable = (LayerDrawable) drawable;
if (layerDrawable.getNumberOfLayers() > 0) {
- if (DEBUG) {
- Slog.d(TAG, "replace drawable with bottom layer drawable");
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "DrawableColorTester: replace drawable with bottom layer drawable");
drawable = layerDrawable.getDrawable(0);
}
}
@@ -805,9 +804,8 @@ public class SplashscreenContentDrawer {
}
}
if (realSize == 0) {
- if (DEBUG) {
- Slog.d(TAG, "quantize: this is pure transparent image");
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "DrawableTester quantize: pure transparent image");
mInnerQuantizer.quantize(pixels, maxColors);
return;
}
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 709e2219a64e..54281e0199e3 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
@@ -52,7 +52,7 @@ import com.android.internal.R;
*/
public class SplashscreenIconDrawableFactory {
- private static final String TAG = "SplashscreenIconDrawableFactory";
+ private static final String TAG = StartingWindowController.TAG;
/**
* @return An array containing the foreground drawable at index 0 and if needed a background
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 270107c01335..9a966b8f9305 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
@@ -61,10 +61,12 @@ import android.window.TaskSnapshot;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.function.Supplier;
@@ -106,9 +108,7 @@ import java.util.function.Supplier;
*/
@ShellSplashscreenThread
public class StartingSurfaceDrawer {
- static final String TAG = StartingSurfaceDrawer.class.getSimpleName();
- static final boolean DEBUG_SPLASH_SCREEN = StartingWindowController.DEBUG_SPLASH_SCREEN;
- static final boolean DEBUG_TASK_SNAPSHOT = StartingWindowController.DEBUG_TASK_SNAPSHOT;
+ private static final String TAG = StartingWindowController.TAG;
private final Context mContext;
private final DisplayManager mDisplayManager;
@@ -179,11 +179,9 @@ public class StartingSurfaceDrawer {
// replace with the default theme if the application didn't set
final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo);
- if (DEBUG_SPLASH_SCREEN) {
- Slog.d(TAG, "addSplashScreen " + activityInfo.packageName
- + " theme=" + Integer.toHexString(theme) + " task=" + taskInfo.taskId
- + " suggestType=" + suggestType);
- }
+ 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.
@@ -208,10 +206,9 @@ public class StartingSurfaceDrawer {
final Configuration taskConfig = taskInfo.getConfiguration();
if (taskConfig.diffPublicOnly(context.getResources().getConfiguration()) != 0) {
- if (DEBUG_SPLASH_SCREEN) {
- Slog.d(TAG, "addSplashScreen: creating context based"
- + " on task Configuration " + taskConfig + " for splash screen");
- }
+ 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(
@@ -222,10 +219,9 @@ public class StartingSurfaceDrawer {
// 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.
- if (DEBUG_SPLASH_SCREEN) {
- Slog.d(TAG, "addSplashScreen: apply overrideConfig"
- + taskConfig + " to starting window resId=" + resId);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "addSplashScreen: apply overrideConfig %s",
+ taskConfig);
context = overrideContext;
}
} catch (Resources.NotFoundException e) {
@@ -273,6 +269,8 @@ public class StartingSurfaceDrawer {
// 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;
@@ -280,9 +278,6 @@ public class StartingSurfaceDrawer {
params.token = appToken;
params.packageName = activityInfo.packageName;
params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- // Setting as trusted overlay to let touches pass through. This is safe because this
- // window is controlled by the system.
- params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
if (!context.getResources().getCompatibilityInfo().supportsScreen()) {
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
@@ -333,7 +328,7 @@ public class StartingSurfaceDrawer {
if (mSysuiProxy != null) {
mSysuiProxy.requestTopUi(true, TAG);
}
- mSplashscreenContentDrawer.createContentView(context, suggestType, activityInfo, taskId,
+ mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo,
viewSupplier::setView, viewSupplier::setUiThreadInitTask);
try {
if (addWindow(taskId, appToken, rootLayout, display, params, suggestType)) {
@@ -461,10 +456,9 @@ public class StartingSurfaceDrawer {
* Called when the content of a task is ready to show, starting window can be removed.
*/
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
- if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
- Slog.d(TAG, "Task start finish, remove starting surface for task "
- + removalInfo.taskId);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Task start finish, remove starting surface for task: %d",
+ removalInfo.taskId);
removeWindowSynced(removalInfo, false /* immediately */);
}
@@ -472,9 +466,8 @@ public class StartingSurfaceDrawer {
* Clear all starting windows immediately.
*/
public void clearAllWindows() {
- if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
- Slog.d(TAG, "Clear all starting windows immediately");
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Clear all starting windows immediately");
final int taskSize = mStartingWindowRecords.size();
final int[] taskIds = new int[taskSize];
for (int i = taskSize - 1; i >= 0; --i) {
@@ -502,10 +495,9 @@ public class StartingSurfaceDrawer {
} else {
parcelable = null;
}
- if (DEBUG_SPLASH_SCREEN) {
- Slog.v(TAG, "Copying splash screen window view for task: " + taskId
- + " parcelable: " + parcelable);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Copying splash screen window view for task: %d with parcelable %b",
+ taskId, parcelable != null);
ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
}
@@ -531,11 +523,9 @@ public class StartingSurfaceDrawer {
return;
}
mAnimatedSplashScreenSurfaceHosts.remove(taskId);
- if (DEBUG_SPLASH_SCREEN) {
- String reason = fromServer ? "Server cleaned up" : "App removed";
- Slog.v(TAG, reason + "the splash screen. Releasing SurfaceControlViewHost for task:"
- + taskId);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "%s the splash screen. Releasing SurfaceControlViewHost for task: %d",
+ fromServer ? "Server cleaned up" : "App removed", taskId);
SplashScreenView.releaseIconHost(viewHost);
}
@@ -593,9 +583,8 @@ public class StartingSurfaceDrawer {
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
if (record != null) {
if (record.mDecorView != null) {
- if (DEBUG_SPLASH_SCREEN) {
- Slog.v(TAG, "Removing splash screen window for task: " + taskId);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Removing splash screen window for task: %d", taskId);
if (record.mContentView != null) {
if (immediately
|| record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
@@ -619,9 +608,8 @@ public class StartingSurfaceDrawer {
mStartingWindowRecords.remove(taskId);
}
if (record.mTaskSnapshotWindow != null) {
- if (DEBUG_TASK_SNAPSHOT) {
- Slog.v(TAG, "Removing task snapshot window for " + taskId);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Removing task snapshot window for %d", taskId);
if (immediately) {
record.mTaskSnapshotWindow.removeImmediately();
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index b0a66059a466..487eb7055e40 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -64,10 +64,7 @@ import com.android.wm.shell.common.TransactionPool;
* @hide
*/
public class StartingWindowController implements RemoteCallable<StartingWindowController> {
- private static final String TAG = StartingWindowController.class.getSimpleName();
-
- public static final boolean DEBUG_SPLASH_SCREEN = false;
- public static final boolean DEBUG_TASK_SNAPSHOT = false;
+ public static final String TAG = "ShellStartingWindow";
private static final long TASK_BG_COLOR_RETAIN_TIME_MS = 5000;
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 3e88c464d359..1bef552e57ca 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
@@ -85,8 +85,10 @@ 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;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
/**
* This class represents a starting window that shows a snapshot.
@@ -113,8 +115,7 @@ public class TaskSnapshotWindow {
| FLAG_SCALED
| FLAG_SECURE;
- private static final String TAG = StartingSurfaceDrawer.TAG;
- private static final boolean DEBUG = StartingSurfaceDrawer.DEBUG_TASK_SNAPSHOT;
+ private static final String TAG = StartingWindowController.TAG;
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
private static final long DELAY_REMOVAL_TIME_GENERAL = 100;
@@ -125,9 +126,6 @@ public class TaskSnapshotWindow {
*/
private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600;
- //tmp vars for unused relayout params
- private static final Point TMP_SURFACE_SIZE = new Point();
-
private final Window mWindow;
private final Runnable mClearWindowHandler;
private final ShellExecutor mSplashScreenExecutor;
@@ -158,9 +156,8 @@ public class TaskSnapshotWindow {
@NonNull Runnable clearWindowHandler) {
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
final int taskId = runningTaskInfo.taskId;
- if (DEBUG) {
- Slog.d(TAG, "create taskSnapshot surface for task: " + taskId);
- }
+ 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;
@@ -244,9 +241,9 @@ public class TaskSnapshotWindow {
window.setOuter(snapshotSurface);
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
- session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
+ session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0,
tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState,
- tmpControls, TMP_SURFACE_SIZE);
+ tmpControls);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} catch (RemoteException e) {
snapshotSurface.clearWindowSynced();
@@ -327,17 +324,15 @@ public class TaskSnapshotWindow {
? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE
: DELAY_REMOVAL_TIME_GENERAL;
mSplashScreenExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime);
- if (DEBUG) {
- Slog.d(TAG, "Defer removing snapshot surface in " + delayRemovalTime);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Defer removing snapshot surface in %d", delayRemovalTime);
}
void removeImmediately() {
mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
try {
- if (DEBUG) {
- Slog.d(TAG, "Removing taskSnapshot surface, mHasDrawn: " + mHasDrawn);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "Removing taskSnapshot surface, mHasDrawn=%b", mHasDrawn);
mSession.remove(mWindow);
} catch (RemoteException e) {
// nothing
@@ -363,9 +358,8 @@ public class TaskSnapshotWindow {
}
private void drawSnapshot() {
- if (DEBUG) {
- Slog.d(TAG, "Drawing snapshot surface sizeMismatch= " + mSizeMismatch);
- }
+ 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
@@ -383,9 +377,7 @@ public class TaskSnapshotWindow {
}
private void drawSizeMatchSnapshot() {
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- mSnapshot.getHardwareBuffer());
- mTransaction.setBuffer(mSurfaceControl, graphicBuffer)
+ mTransaction.setBuffer(mSurfaceControl, mSnapshot.getHardwareBuffer())
.setColorSpace(mSurfaceControl, mSnapshot.getColorSpace())
.apply();
}
@@ -431,20 +423,20 @@ public class TaskSnapshotWindow {
// Scale the mismatch dimensions to fill the task bounds
mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9);
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- mSnapshot.getHardwareBuffer());
mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
- mTransaction.setBuffer(childSurfaceControl, graphicBuffer);
+ 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, background);
+ mTransaction.setBuffer(mSurfaceControl,
+ HardwareBuffer.createFromGraphicBuffer(background));
}
mTransaction.apply();
childSurfaceControl.release();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index bde2b5ff4d60..51a48a265f02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -23,6 +23,7 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
@@ -30,22 +31,18 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN;
-import static com.android.wm.shell.startingsurface.StartingWindowController.DEBUG_SPLASH_SCREEN;
-import static com.android.wm.shell.startingsurface.StartingWindowController.DEBUG_TASK_SNAPSHOT;
-
-import android.util.Slog;
import android.window.StartingWindowInfo;
import android.window.TaskSnapshot;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
/**
* Algorithm for determining the type of a new starting window on handheld devices.
- * At the moment also used on Android Auto.
+ * At the moment also used on Android Auto and Wear OS.
*/
public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgorithm {
- private static final String TAG = PhoneStartingWindowTypeAlgorithm.class.getSimpleName();
-
@Override
public int getSuggestedWindowType(StartingWindowInfo windowInfo) {
final int parameter = windowInfo.startingWindowTypeParameter;
@@ -58,39 +55,52 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
(parameter & TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN) != 0;
final boolean legacySplashScreen =
((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
+ final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME;
- if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
- Slog.d(TAG, "preferredStartingWindowType newTask:" + newTask
- + " taskSwitch:" + taskSwitch
- + " processRunning:" + processRunning
- + " allowTaskSnapshot:" + allowTaskSnapshot
- + " activityCreated:" + activityCreated
- + " useEmptySplashScreen:" + useEmptySplashScreen
- + " legacySplashScreen:" + legacySplashScreen
- + " topIsHome:" + topIsHome);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "preferredStartingWindowType "
+ + "newTask=%b, "
+ + "taskSwitch=%b, "
+ + "processRunning=%b, "
+ + "allowTaskSnapshot=%b, "
+ + "activityCreated=%b, "
+ + "useEmptySplashScreen=%b, "
+ + "legacySplashScreen=%b, "
+ + "activityDrawn=%b, "
+ + "topIsHome=%b",
+ newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated,
+ useEmptySplashScreen, legacySplashScreen, activityDrawn, topIsHome);
if (!topIsHome) {
if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
- return useEmptySplashScreen
- ? STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN
- : legacySplashScreen
- ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
- : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ return getSplashscreenType(useEmptySplashScreen, legacySplashScreen);
}
}
- if (taskSwitch && allowTaskSnapshot) {
- if (isSnapshotCompatible(windowInfo)) {
- return STARTING_WINDOW_TYPE_SNAPSHOT;
+
+ if (taskSwitch) {
+ if (allowTaskSnapshot) {
+ if (isSnapshotCompatible(windowInfo)) {
+ return STARTING_WINDOW_TYPE_SNAPSHOT;
+ }
+ if (!topIsHome) {
+ return STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
+ }
}
- if (!topIsHome) {
- return STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
+ if (!activityDrawn && !topIsHome) {
+ return getSplashscreenType(useEmptySplashScreen, legacySplashScreen);
}
}
return STARTING_WINDOW_TYPE_NONE;
}
+ private static int getSplashscreenType(boolean emptySplashScreen, boolean legacySplashScreen) {
+ return emptySplashScreen
+ ? STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN
+ : legacySplashScreen
+ ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+ : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ }
/**
* Returns {@code true} if the task snapshot is compatible with this activity (at least the
@@ -99,26 +109,24 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
private boolean isSnapshotCompatible(StartingWindowInfo windowInfo) {
final TaskSnapshot snapshot = windowInfo.taskSnapshot;
if (snapshot == null) {
- if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
- Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "isSnapshotCompatible no snapshot, taskId=%d",
+ windowInfo.taskInfo.taskId);
return false;
}
if (!snapshot.getTopActivityComponent().equals(windowInfo.taskInfo.topActivity)) {
- if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
- Slog.d(TAG, "isSnapshotCompatible obsoleted snapshot "
- + windowInfo.taskInfo.topActivity);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "isSnapshotCompatible obsoleted snapshot for %s",
+ windowInfo.taskInfo.topActivity);
return false;
}
final int taskRotation = windowInfo.taskInfo.configuration
.windowConfiguration.getRotation();
final int snapshotRotation = snapshot.getRotation();
- if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
- Slog.d(TAG, "isSnapshotCompatible rotation " + taskRotation
- + " snapshot " + snapshotRotation);
- }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "isSnapshotCompatible taskRotation=%d, snapshotRotation=%d",
+ taskRotation, snapshotRotation);
return taskRotation == snapshotRotation;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
new file mode 100644
index 000000000000..19133e29de4b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java
@@ -0,0 +1,109 @@
+/*
+ * 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.transition;
+
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+
+import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.util.RotationUtils;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.util.CounterRotator;
+
+import java.util.List;
+
+/**
+ * The helper class that performs counter-rotate for all "going-away" window containers if they are
+ * still in the old rotation in a transition.
+ */
+public class CounterRotatorHelper {
+ private final ArrayMap<WindowContainerToken, CounterRotator> mRotatorMap = new ArrayMap<>();
+ private final Rect mLastDisplayBounds = new Rect();
+ private int mLastRotationDelta;
+
+ /** Puts the surface controls of closing changes to counter-rotated surfaces. */
+ public void handleClosingChanges(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull TransitionInfo.Change displayRotationChange) {
+ final int rotationDelta = RotationUtils.deltaRotation(
+ displayRotationChange.getStartRotation(), displayRotationChange.getEndRotation());
+ final Rect displayBounds = displayRotationChange.getEndAbsBounds();
+ final int displayW = displayBounds.width();
+ final int displayH = displayBounds.height();
+ mLastRotationDelta = rotationDelta;
+ mLastDisplayBounds.set(displayBounds);
+
+ final List<TransitionInfo.Change> changes = info.getChanges();
+ final int numChanges = changes.size();
+ for (int i = numChanges - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = changes.get(i);
+ final WindowContainerToken parent = change.getParent();
+ if (!Transitions.isClosingType(change.getMode())
+ || !TransitionInfo.isIndependent(change, info) || parent == null) {
+ continue;
+ }
+
+ CounterRotator crot = mRotatorMap.get(parent);
+ if (crot == null) {
+ crot = new CounterRotator();
+ crot.setup(startTransaction, info.getChange(parent).getLeash(), rotationDelta,
+ displayW, displayH);
+ final SurfaceControl rotatorSc = crot.getSurface();
+ if (rotatorSc != null) {
+ // Wallpaper should be placed at the bottom.
+ final int layer = (change.getFlags() & FLAG_IS_WALLPAPER) == 0
+ ? numChanges - i
+ : -1;
+ startTransaction.setLayer(rotatorSc, layer);
+ }
+ mRotatorMap.put(parent, crot);
+ }
+ crot.addChild(startTransaction, change.getLeash());
+ }
+ }
+
+ /**
+ * Returns the rotated end bounds if the change is put in previous rotation. Otherwise the
+ * original end bounds are returned.
+ */
+ @NonNull
+ public Rect getEndBoundsInStartRotation(@NonNull TransitionInfo.Change change) {
+ if (mLastRotationDelta == 0) return change.getEndAbsBounds();
+ final Rect rotatedBounds = new Rect(change.getEndAbsBounds());
+ RotationUtils.rotateBounds(rotatedBounds, mLastDisplayBounds, mLastRotationDelta);
+ return rotatedBounds;
+ }
+
+ /**
+ * Removes the counter rotation surface in the finish transaction. No need to reparent the
+ * children as the finish transaction should have already taken care of that.
+ *
+ * This can only be called after startTransaction for {@link #handleClosingChanges} is applied.
+ */
+ public void cleanUp(@NonNull SurfaceControl.Transaction finishTransaction) {
+ for (int i = mRotatorMap.size() - 1; i >= 0; --i) {
+ mRotatorMap.valueAt(i).cleanUp(finishTransaction);
+ }
+ mRotatorMap.clear();
+ mLastRotationDelta = 0;
+ }
+}
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 7abda994bb5e..ddf01a8c5ee9 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
@@ -18,11 +18,18 @@ package com.android.wm.shell.transition;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_FROM_STYLE;
import static android.app.ActivityOptions.ANIM_NONE;
import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
+import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE;
+import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
@@ -41,7 +48,6 @@ import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
-import static android.window.TransitionInfo.isIndependent;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
@@ -52,32 +58,47 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
+import android.os.Handler;
import android.os.IBinder;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.view.Choreographer;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
+import android.view.WindowManager.TransitionType;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.window.TransitionInfo;
import android.window.TransitionMetrics;
import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.AttributeCache;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.DisplayController;
@@ -85,9 +106,10 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.util.CounterRotator;
import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
/** The default handler that handles anything not already handled. */
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
@@ -114,12 +136,14 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionAnimation mTransitionAnimation;
+ private final DevicePolicyManager mDevicePolicyManager;
private final SurfaceSession mSurfaceSession = new SurfaceSession();
/** Keeps track of the currently-running animations associated with each transition. */
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
+ private final CounterRotatorHelper mRotator = new CounterRotatorHelper();
private final Rect mInsets = new Rect(0, 0, 0, 0);
private float mTransitionAnimationScaleSetting = 1.0f;
@@ -127,9 +151,24 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private ScreenRotationAnimation mRotationAnimation;
+ private Drawable mEnterpriseThumbnailDrawable;
+
+ private BroadcastReceiver mEnterpriseResourceUpdatedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean isDrawable = intent.getBooleanExtra(
+ EXTRA_RESOURCE_TYPE_DRAWABLE, /* default= */ false);
+ if (!isDrawable) {
+ return;
+ }
+ updateEnterpriseThumbnailDrawable();
+ }
+ };
+
DefaultTransitionHandler(@NonNull DisplayController displayController,
@NonNull TransactionPool transactionPool, Context context,
- @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
+ @NonNull ShellExecutor animExecutor) {
mDisplayController = displayController;
mTransactionPool = transactionPool;
mContext = context;
@@ -138,9 +177,23 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
mCurrentUserId = UserHandle.myUserId();
+ mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
+ updateEnterpriseThumbnailDrawable();
+ mContext.registerReceiver(
+ mEnterpriseResourceUpdatedReceiver,
+ new IntentFilter(ACTION_DEVICE_POLICY_RESOURCE_UPDATED),
+ /* broadcastPermission = */ null,
+ mainHandler);
+
AttributeCache.init(context);
}
+ private void updateEnterpriseThumbnailDrawable() {
+ mEnterpriseThumbnailDrawable = mDevicePolicyManager.getDrawable(
+ WORK_PROFILE_ICON, OUTLINE, PROFILE_SWITCH_ANIMATION,
+ () -> mContext.getDrawable(R.drawable.ic_corp_badge));
+ }
+
@VisibleForTesting
static boolean isRotationSeamless(@NonNull TransitionInfo info,
DisplayController displayController) {
@@ -273,16 +326,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final ArrayList<Animator> animations = new ArrayList<>();
mAnimations.put(transition, animations);
- final ArrayMap<WindowContainerToken, CounterRotator> counterRotators = new ArrayMap<>();
-
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
- for (int i = 0; i < counterRotators.size(); ++i) {
- counterRotators.valueAt(i).cleanUp(info.getRootLeash());
- }
- counterRotators.clear();
-
if (mRotationAnimation != null) {
mRotationAnimation.kill();
mRotationAnimation = null;
@@ -292,57 +338,54 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
+ final List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks =
+ new ArrayList<>();
+
+ @ColorInt int backgroundColorForTransition = 0;
final int wallpaperTransit = getWallpaperTransitType(info);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
+ final boolean isTask = change.getTaskInfo() != null;
if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
- int rotateDelta = change.getEndRotation() - change.getStartRotation();
- int displayW = change.getEndAbsBounds().width();
- int displayH = change.getEndAbsBounds().height();
if (info.getType() == TRANSIT_CHANGE) {
boolean isSeamless = isRotationSeamless(info, mDisplayController);
final int anim = getRotationAnimation(info);
if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
- mTransactionPool, startTransaction, change, info.getRootLeash());
+ mTransactionPool, startTransaction, change, info.getRootLeash(),
+ anim);
mRotationAnimation.startAnimation(animations, onAnimFinish,
mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
continue;
}
} else {
- // opening/closing an app into a new orientation. Counter-rotate all
- // "going-away" things since they are still in the old orientation.
- for (int j = info.getChanges().size() - 1; j >= 0; --j) {
- final TransitionInfo.Change innerChange = info.getChanges().get(j);
- if (!Transitions.isClosingType(innerChange.getMode())
- || !isIndependent(innerChange, info)
- || innerChange.getParent() == null) {
- continue;
- }
- CounterRotator crot = counterRotators.get(innerChange.getParent());
- if (crot == null) {
- crot = new CounterRotator();
- crot.setup(startTransaction,
- info.getChange(innerChange.getParent()).getLeash(),
- rotateDelta, displayW, displayH);
- if (crot.getSurface() != null) {
- int layer = info.getChanges().size() - j;
- startTransaction.setLayer(crot.getSurface(), layer);
- }
- counterRotators.put(innerChange.getParent(), crot);
- }
- crot.addChild(startTransaction, innerChange.getLeash());
- }
+ // Opening/closing an app into a new orientation.
+ mRotator.handleClosingChanges(info, startTransaction, change);
}
}
if (change.getMode() == TRANSIT_CHANGE) {
+ // If task is child task, only set position in parent.
+ if (isTask && change.getParent() != null
+ && info.getChange(change.getParent()).getTaskInfo() != null) {
+ final Point positionInParent = change.getTaskInfo().positionInParent;
+ startTransaction.setPosition(change.getLeash(),
+ positionInParent.x, positionInParent.y);
+ continue;
+ }
+
+ // There is no default animation for Pip window in rotation transition, and the
+ // PipTransition will update the surface of its own window at start/finish.
+ if (isTask && change.getTaskInfo().configuration.windowConfiguration
+ .getWindowingMode() == WINDOWING_MODE_PINNED) {
+ continue;
+ }
// No default animation for this, so just update bounds/position.
startTransaction.setPosition(change.getLeash(),
- change.getEndAbsBounds().left - change.getEndRelOffset().x,
- change.getEndAbsBounds().top - change.getEndRelOffset().y);
- if (change.getTaskInfo() != null) {
+ change.getEndAbsBounds().left - info.getRootOffset().x,
+ change.getEndAbsBounds().top - info.getRootOffset().y);
+ if (isTask) {
// Skip non-tasks since those usually have null bounds.
startTransaction.setWindowCrop(change.getLeash(),
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
@@ -354,21 +397,244 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
Animation a = loadAnimation(info, change, wallpaperTransit);
if (a != null) {
- startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
- mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */);
+ if (isTask) {
+ final @TransitionType int type = info.getType();
+ final boolean isOpenOrCloseTransition = type == TRANSIT_OPEN
+ || type == TRANSIT_CLOSE
+ || type == TRANSIT_TO_FRONT
+ || type == TRANSIT_TO_BACK;
+ if (isOpenOrCloseTransition) {
+ // Use the overview background as the background for the animation
+ final Context uiContext = ActivityThread.currentActivityThread()
+ .getSystemUiContext();
+ backgroundColorForTransition =
+ uiContext.getColor(R.color.overview_background);
+ }
+ }
+
+ final float cornerRadius;
+ if (a.hasRoundedCorners() && isTask) {
+ // hasRoundedCorners is currently only enabled for tasks
+ final Context displayContext =
+ mDisplayController.getDisplayContext(change.getTaskInfo().displayId);
+ cornerRadius =
+ ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
+ } else {
+ cornerRadius = 0;
+ }
+
+ if (a.getShowBackground()) {
+ if (info.getAnimationOptions().getBackgroundColor() != 0) {
+ // If available use the background color provided through AnimationOptions
+ backgroundColorForTransition =
+ info.getAnimationOptions().getBackgroundColor();
+ } else if (change.getBackgroundColor() != 0) {
+ // Otherwise default to the window's background color if provided through
+ // the theme as the background color for the animation - the top most window
+ // with a valid background color and showBackground set takes precedence.
+ backgroundColorForTransition = change.getBackgroundColor();
+ }
+ }
+
+ boolean delayedEdgeExtension = false;
+ if (!isTask && a.hasExtension()) {
+ if (!Transitions.isOpeningType(change.getMode())) {
+ // Can screenshot now (before startTransaction is applied)
+ edgeExtendWindow(change, a, startTransaction, finishTransaction);
+ } else {
+ // Need to screenshot after startTransaction is applied otherwise activity
+ // may not be visible or ready yet.
+ postStartTransactionCallbacks
+ .add(t -> edgeExtendWindow(change, a, t, finishTransaction));
+ delayedEdgeExtension = true;
+ }
+ }
+
+ final Rect clipRect = Transitions.isClosingType(change.getMode())
+ ? mRotator.getEndBoundsInStartRotation(change)
+ : change.getEndAbsBounds();
+
+ if (delayedEdgeExtension) {
+ // If the edge extension needs to happen after the startTransition has been
+ // applied, then we want to only start the animation after the edge extension
+ // postStartTransaction callback has been run
+ postStartTransactionCallbacks.add(t ->
+ startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+ mTransactionPool, mMainExecutor, mAnimExecutor,
+ null /* position */, cornerRadius, clipRect));
+ } else {
+ startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+ mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
+ cornerRadius, clipRect);
+ }
if (info.getAnimationOptions() != null) {
- attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions());
+ attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
+ cornerRadius);
}
}
}
- startTransaction.apply();
+
+ if (backgroundColorForTransition != 0) {
+ addBackgroundToTransition(info.getRootLeash(), backgroundColorForTransition,
+ startTransaction, finishTransaction);
+ }
+
+ // postStartTransactionCallbacks require that the start transaction is already
+ // applied to run otherwise they may result in flickers and UI inconsistencies.
+ boolean waitForStartTransactionApply = postStartTransactionCallbacks.size() > 0;
+ startTransaction.apply(waitForStartTransactionApply);
+
+ // Run tasks that require startTransaction to already be applied
+ for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback :
+ postStartTransactionCallbacks) {
+ final SurfaceControl.Transaction t = mTransactionPool.acquire();
+ postStartTransactionCallback.accept(t);
+ t.apply();
+ mTransactionPool.release(t);
+ }
+
+ mRotator.cleanUp(finishTransaction);
TransitionMetrics.getInstance().reportAnimationStart(transition);
// run finish now in-case there are no animations
onAnimFinish.run();
return true;
}
+ private void edgeExtendWindow(TransitionInfo.Change change,
+ Animation a, SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction) {
+ final Transformation transformationAtStart = new Transformation();
+ a.getTransformationAt(0, transformationAtStart);
+ final Transformation transformationAtEnd = new Transformation();
+ a.getTransformationAt(1, transformationAtEnd);
+
+ // We want to create an extension surface that is the maximal size and the animation will
+ // take care of cropping any part that overflows.
+ final Insets maxExtensionInsets = Insets.min(
+ transformationAtStart.getInsets(), transformationAtEnd.getInsets());
+
+ final int targetSurfaceHeight = Math.max(change.getStartAbsBounds().height(),
+ change.getEndAbsBounds().height());
+ final int targetSurfaceWidth = Math.max(change.getStartAbsBounds().width(),
+ change.getEndAbsBounds().width());
+ if (maxExtensionInsets.left < 0) {
+ final Rect edgeBounds = new Rect(0, 0, 1, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ -maxExtensionInsets.left, targetSurfaceHeight);
+ final int xPos = maxExtensionInsets.left;
+ final int yPos = 0;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Left Edge Extension", startTransaction, finishTransaction);
+ }
+
+ if (maxExtensionInsets.top < 0) {
+ final Rect edgeBounds = new Rect(0, 0, targetSurfaceWidth, 1);
+ final Rect extensionRect = new Rect(0, 0,
+ targetSurfaceWidth, -maxExtensionInsets.top);
+ final int xPos = 0;
+ final int yPos = maxExtensionInsets.top;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Top Edge Extension", startTransaction, finishTransaction);
+ }
+
+ if (maxExtensionInsets.right < 0) {
+ final Rect edgeBounds = new Rect(targetSurfaceWidth - 1, 0,
+ targetSurfaceWidth, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ -maxExtensionInsets.right, targetSurfaceHeight);
+ final int xPos = targetSurfaceWidth;
+ final int yPos = 0;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Right Edge Extension", startTransaction, finishTransaction);
+ }
+
+ if (maxExtensionInsets.bottom < 0) {
+ final Rect edgeBounds = new Rect(0, targetSurfaceHeight - 1,
+ targetSurfaceWidth, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ targetSurfaceWidth, -maxExtensionInsets.bottom);
+ final int xPos = maxExtensionInsets.left;
+ final int yPos = targetSurfaceHeight;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Bottom Edge Extension", startTransaction, finishTransaction);
+ }
+ }
+
+ private SurfaceControl createExtensionSurface(SurfaceControl surfaceToExtend, Rect edgeBounds,
+ Rect extensionRect, int xPos, int yPos, String layerName,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction) {
+ final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder()
+ .setName(layerName)
+ .setParent(surfaceToExtend)
+ .setHidden(true)
+ .setCallsite("DefaultTransitionHandler#startAnimation")
+ .setOpaque(true)
+ .setBufferSize(extensionRect.width(), extensionRect.height())
+ .build();
+
+ SurfaceControl.LayerCaptureArgs captureArgs =
+ new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend)
+ .setSourceCrop(edgeBounds)
+ .setFrameScale(1)
+ .setPixelFormat(PixelFormat.RGBA_8888)
+ .setChildrenOnly(true)
+ .setAllowProtected(true)
+ .build();
+ final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
+ SurfaceControl.captureLayers(captureArgs);
+
+ if (edgeBuffer == null) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Failed to capture edge of window.");
+ return null;
+ }
+
+ android.graphics.BitmapShader shader =
+ new android.graphics.BitmapShader(edgeBuffer.asBitmap(),
+ android.graphics.Shader.TileMode.CLAMP,
+ android.graphics.Shader.TileMode.CLAMP);
+ final Paint paint = new Paint();
+ paint.setShader(shader);
+
+ final Surface surface = new Surface(edgeExtensionLayer);
+ Canvas c = surface.lockHardwareCanvas();
+ c.drawRect(extensionRect, paint);
+ surface.unlockCanvasAndPost(c);
+ surface.release();
+
+ startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE);
+ startTransaction.setPosition(edgeExtensionLayer, xPos, yPos);
+ startTransaction.setVisibility(edgeExtensionLayer, true);
+ finishTransaction.remove(edgeExtensionLayer);
+
+ return edgeExtensionLayer;
+ }
+
+ private void addBackgroundToTransition(
+ @NonNull SurfaceControl rootLeash,
+ @ColorInt int color,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction
+ ) {
+ final Color bgColor = Color.valueOf(color);
+ final float[] colorArray = new float[] { bgColor.red(), bgColor.green(), bgColor.blue() };
+
+ final SurfaceControl animationBackgroundSurface = new SurfaceControl.Builder()
+ .setName("Animation Background")
+ .setParent(rootLeash)
+ .setColorLayer()
+ .setOpaque(true)
+ .build();
+
+ startTransaction
+ .setLayer(animationBackgroundSurface, Integer.MIN_VALUE)
+ .setColor(animationBackgroundSurface, colorArray)
+ .show(animationBackgroundSurface);
+ finishTransaction.remove(animationBackgroundSurface);
+ }
+
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@@ -396,6 +662,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
final int overrideType = options != null ? options.getType() : ANIM_NONE;
final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true;
+ final Rect endBounds = Transitions.isClosingType(changeMode)
+ ? mRotator.getEndBoundsInStartRotation(change)
+ : change.getEndAbsBounds();
if (info.isKeyguardGoingAway()) {
a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
@@ -413,8 +682,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a = new AlphaAnimation(1.f, 1.f);
a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION);
} else if (type == TRANSIT_RELAUNCH) {
- a = mTransitionAnimation.createRelaunchAnimation(
- change.getEndAbsBounds(), mInsets, change.getEndAbsBounds());
+ a = mTransitionAnimation.createRelaunchAnimation(endBounds, mInsets, endBounds);
} else if (overrideType == ANIM_CUSTOM
&& (canCustomContainer || options.getOverrideTaskTransition())) {
a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter
@@ -423,80 +691,90 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
} else if (overrideType == ANIM_CLIP_REVEAL) {
a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter,
- change.getEndAbsBounds(), change.getEndAbsBounds(),
- options.getTransitionBounds());
+ endBounds, endBounds, options.getTransitionBounds());
} else if (overrideType == ANIM_SCALE_UP) {
a = mTransitionAnimation.createScaleUpAnimationLocked(type, wallpaperTransit, enter,
- change.getEndAbsBounds(), options.getTransitionBounds());
+ endBounds, options.getTransitionBounds());
} else if (overrideType == ANIM_THUMBNAIL_SCALE_UP
|| overrideType == ANIM_THUMBNAIL_SCALE_DOWN) {
final boolean scaleUp = overrideType == ANIM_THUMBNAIL_SCALE_UP;
a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(enter, scaleUp,
- change.getEndAbsBounds(), type, wallpaperTransit, options.getThumbnail(),
+ endBounds, type, wallpaperTransit, options.getThumbnail(),
options.getTransitionBounds());
} else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) {
// This received a transferred starting window, so don't animate
return null;
- } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(enter
- ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
- : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation);
- } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(enter
- ? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
- : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation);
- } else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(enter
- ? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
- : R.styleable.WindowAnimation_wallpaperOpenExitAnimation);
- } else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(enter
- ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
- : R.styleable.WindowAnimation_wallpaperCloseExitAnimation);
- } else if (type == TRANSIT_OPEN) {
- if (isTask) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(enter
- ? R.styleable.WindowAnimation_taskOpenEnterAnimation
- : R.styleable.WindowAnimation_taskOpenExitAnimation);
- } else {
- if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
- a = mTransitionAnimation.loadDefaultAnimationRes(
- R.anim.activity_translucent_open_enter);
+ } else {
+ int animAttr = 0;
+ boolean translucent = false;
+ if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+ animAttr = enter
+ ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
+ animAttr = enter
+ ? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) {
+ animAttr = enter
+ ? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) {
+ animAttr = enter
+ ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
+ } else if (type == TRANSIT_OPEN) {
+ if (isTask) {
+ animAttr = enter
+ ? R.styleable.WindowAnimation_taskOpenEnterAnimation
+ : R.styleable.WindowAnimation_taskOpenExitAnimation;
} else {
- a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
+ translucent = true;
+ }
+ animAttr = enter
? R.styleable.WindowAnimation_activityOpenEnterAnimation
- : R.styleable.WindowAnimation_activityOpenExitAnimation);
+ : R.styleable.WindowAnimation_activityOpenExitAnimation;
}
- }
- } else if (type == TRANSIT_TO_FRONT) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(enter
- ? R.styleable.WindowAnimation_taskToFrontEnterAnimation
- : R.styleable.WindowAnimation_taskToFrontExitAnimation);
- } else if (type == TRANSIT_CLOSE) {
- if (isTask) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(enter
- ? R.styleable.WindowAnimation_taskCloseEnterAnimation
- : R.styleable.WindowAnimation_taskCloseExitAnimation);
- } else {
- if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
- a = mTransitionAnimation.loadDefaultAnimationRes(
- R.anim.activity_translucent_close_exit);
+ } else if (type == TRANSIT_TO_FRONT) {
+ animAttr = enter
+ ? R.styleable.WindowAnimation_taskToFrontEnterAnimation
+ : R.styleable.WindowAnimation_taskToFrontExitAnimation;
+ } else if (type == TRANSIT_CLOSE) {
+ if (isTask) {
+ animAttr = enter
+ ? R.styleable.WindowAnimation_taskCloseEnterAnimation
+ : R.styleable.WindowAnimation_taskCloseExitAnimation;
} else {
- a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
+ translucent = true;
+ }
+ animAttr = enter
? R.styleable.WindowAnimation_activityCloseEnterAnimation
- : R.styleable.WindowAnimation_activityCloseExitAnimation);
+ : R.styleable.WindowAnimation_activityCloseExitAnimation;
+ }
+ } else if (type == TRANSIT_TO_BACK) {
+ animAttr = enter
+ ? R.styleable.WindowAnimation_taskToBackEnterAnimation
+ : R.styleable.WindowAnimation_taskToBackExitAnimation;
+ }
+
+ if (animAttr != 0) {
+ if (overrideType == ANIM_FROM_STYLE && canCustomContainer) {
+ a = mTransitionAnimation
+ .loadAnimationAttr(options.getPackageName(), options.getAnimations(),
+ animAttr, translucent);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(animAttr);
}
}
- } else if (type == TRANSIT_TO_BACK) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(enter
- ? R.styleable.WindowAnimation_taskToBackEnterAnimation
- : R.styleable.WindowAnimation_taskToBackExitAnimation);
}
if (a != null) {
if (!a.isInitialized()) {
- Rect end = change.getEndAbsBounds();
- a.initialize(end.width(), end.height(), end.width(), end.height());
+ final int width = endBounds.width();
+ final int height = endBounds.height();
+ a.initialize(width, height, width, height);
}
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
@@ -508,7 +786,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
@NonNull Animation anim, @NonNull SurfaceControl leash,
@NonNull Runnable finishCallback, @NonNull TransactionPool pool,
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor,
- @Nullable Point position) {
+ @Nullable Point position, float cornerRadius, @Nullable Rect clipRect) {
final SurfaceControl.Transaction transaction = pool.acquire();
final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
final Transformation transformation = new Transformation();
@@ -520,12 +798,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
- position);
+ position, cornerRadius, clipRect);
});
final Runnable finisher = () -> {
applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
- position);
+ position, cornerRadius, clipRect);
pool.release(transaction);
mainExecutor.execute(() -> {
@@ -550,28 +828,30 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private void attachThumbnail(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, TransitionInfo.Change change,
- TransitionInfo.AnimationOptions options) {
+ TransitionInfo.AnimationOptions options, float cornerRadius) {
final boolean isTask = change.getTaskInfo() != null;
final boolean isOpen = Transitions.isOpeningType(change.getMode());
final boolean isClose = Transitions.isClosingType(change.getMode());
if (isOpen) {
if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
- attachCrossProfileThunmbnailAnimation(animations, finishCallback, change);
+ attachCrossProfileThumbnailAnimation(animations, finishCallback, change,
+ cornerRadius);
} else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
- attachThumbnailAnimation(animations, finishCallback, change, options);
+ attachThumbnailAnimation(animations, finishCallback, change, options, cornerRadius);
}
} else if (isClose && options.getType() == ANIM_THUMBNAIL_SCALE_DOWN) {
- attachThumbnailAnimation(animations, finishCallback, change, options);
+ attachThumbnailAnimation(animations, finishCallback, change, options, cornerRadius);
}
}
- private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations,
- @NonNull Runnable finishCallback, TransitionInfo.Change change) {
- final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId
- ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge;
+ private void attachCrossProfileThumbnailAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, TransitionInfo.Change change, float cornerRadius) {
final Rect bounds = change.getEndAbsBounds();
+ // Show the right drawable depending on the user we're transitioning to.
+ final Drawable thumbnailDrawable = change.getTaskInfo().userId == mCurrentUserId
+ ? mContext.getDrawable(R.drawable.ic_account_circle) : mEnterpriseThumbnailDrawable;
final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail(
- thumbnailDrawableRes, bounds);
+ thumbnailDrawable, bounds);
if (thumbnail == null) {
return;
}
@@ -594,12 +874,13 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top));
+ mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top),
+ cornerRadius, change.getEndAbsBounds());
}
private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, TransitionInfo.Change change,
- TransitionInfo.AnimationOptions options) {
+ TransitionInfo.AnimationOptions options, float cornerRadius) {
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
change.getLeash(), options.getThumbnail(), transaction);
@@ -618,7 +899,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, mAnimExecutor, null /* position */);
+ mMainExecutor, mAnimExecutor, null /* position */,
+ cornerRadius, change.getEndAbsBounds());
}
private static int getWallpaperTransitType(TransitionInfo info) {
@@ -650,13 +932,27 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private static void applyTransformation(long time, SurfaceControl.Transaction t,
SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix,
- Point position) {
+ Point position, float cornerRadius, @Nullable Rect clipRect) {
anim.getTransformation(time, transformation);
if (position != null) {
transformation.getMatrix().postTranslate(position.x, position.y);
}
t.setMatrix(leash, transformation.getMatrix(), matrix);
t.setAlpha(leash, transformation.getAlpha());
+
+ Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
+ if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) {
+ // Clip out any overflowing edge extension
+ clipRect.inset(extensionInsets);
+ t.setCrop(leash, clipRect);
+ }
+
+ if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) {
+ // We can only apply rounded corner if a crop is set
+ t.setCrop(leash, clipRect);
+ t.setCornerRadius(leash, cornerRadius);
+ }
+
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
t.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 13c670a1ab1e..46f73fda37a1 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
@@ -19,6 +19,8 @@ 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;
import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE;
import static com.android.wm.shell.transition.DefaultTransitionHandler.startSurfaceAnimation;
@@ -59,7 +61,7 @@ import java.util.Arrays;
* This class handles the rotation animation when the device is rotated.
*
* <p>
- * The screen rotation animation is composed of 4 different part:
+ * The screen rotation animation is composed of 3 different part:
* <ul>
* <li> The screenshot: <p>
* A screenshot of the whole screen prior the change of orientation is taken to hide the
@@ -75,10 +77,6 @@ import java.util.Arrays;
* To have the animation seem more seamless, we add a color transitioning background behind the
* exiting and entering layouts. We compute the brightness of the start and end
* layouts and transition from the two brightness values as grayscale underneath the animation
- *
- * <li> The entering Blackframe: <p>
- * The enter Blackframe is similar to the exit Blackframe but is only used when a custom
- * rotation animation is used and matches the new content size instead of the screenshot.
* </ul>
*/
class ScreenRotationAnimation {
@@ -94,6 +92,7 @@ class ScreenRotationAnimation {
private final Rect mStartBounds = new Rect();
private final Rect mEndBounds = new Rect();
+ private final int mAnimHint;
private final int mStartWidth;
private final int mStartHeight;
private final int mEndWidth;
@@ -117,6 +116,7 @@ class ScreenRotationAnimation {
// rotations.
private Animation mRotateExitAnimation;
private Animation mRotateEnterAnimation;
+ private Animation mRotateAlphaAnimation;
/** Intensity of light/whiteness of the layout before rotation occurs. */
private float mStartLuma;
@@ -124,9 +124,10 @@ class ScreenRotationAnimation {
private float mEndLuma;
ScreenRotationAnimation(Context context, SurfaceSession session, TransactionPool pool,
- Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash) {
+ Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash, int animHint) {
mContext = context;
mTransactionPool = pool;
+ mAnimHint = animHint;
mSurfaceControl = change.getLeash();
mStartWidth = change.getStartAbsBounds().width();
@@ -160,13 +161,6 @@ class ScreenRotationAnimation {
return;
}
- mBackColorSurface = new SurfaceControl.Builder(session)
- .setParent(rootLeash)
- .setColorLayer()
- .setCallsite("ShellRotationAnimation")
- .setName("BackColorSurface")
- .build();
-
mScreenshotLayer = new SurfaceControl.Builder(session)
.setParent(mAnimLeash)
.setBLASTLayer()
@@ -175,17 +169,9 @@ class ScreenRotationAnimation {
.setName("RotationLayer")
.build();
- HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
- mStartLuma = getMedianBorderLuma(hardwareBuffer, screenshotBuffer.getColorSpace());
-
GraphicBuffer buffer = GraphicBuffer.createFromHardwareBuffer(
screenshotBuffer.getHardwareBuffer());
- t.setLayer(mBackColorSurface, -1);
- t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
- t.setAlpha(mBackColorSurface, 1);
- t.show(mBackColorSurface);
-
t.setLayer(mAnimLeash, SCREEN_FREEZE_LAYER_BASE);
t.setPosition(mAnimLeash, 0, 0);
t.setAlpha(mAnimLeash, 1);
@@ -195,6 +181,23 @@ class ScreenRotationAnimation {
t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace());
t.show(mScreenshotLayer);
+ if (!isCustomRotate()) {
+ mBackColorSurface = new SurfaceControl.Builder(session)
+ .setParent(rootLeash)
+ .setColorLayer()
+ .setCallsite("ShellRotationAnimation")
+ .setName("BackColorSurface")
+ .build();
+
+ HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+ mStartLuma = getMedianBorderLuma(hardwareBuffer, screenshotBuffer.getColorSpace());
+
+ t.setLayer(mBackColorSurface, -1);
+ t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
+ t.setAlpha(mBackColorSurface, 1);
+ t.show(mBackColorSurface);
+ }
+
} catch (Surface.OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
@@ -203,6 +206,10 @@ class ScreenRotationAnimation {
t.apply();
}
+ private boolean isCustomRotate() {
+ return mAnimHint == ROTATION_ANIMATION_CROSSFADE || mAnimHint == ROTATION_ANIMATION_JUMPCUT;
+ }
+
private void setRotation(SurfaceControl.Transaction t) {
// Compute the transformation matrix that must be applied
// to the snapshot to make it stay in the same original position
@@ -244,33 +251,44 @@ class ScreenRotationAnimation {
// color frame animation.
//mEndLuma = getLumaOfSurfaceControl(mEndBounds, mSurfaceControl);
- // Figure out how the screen has moved from the original rotation.
- int delta = deltaRotation(mEndRotation, mStartRotation);
- switch (delta) { /* Counter-Clockwise Rotations */
- case Surface.ROTATION_0:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_0_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.rotation_animation_enter);
- break;
- case Surface.ROTATION_90:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_plus_90_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_plus_90_enter);
- break;
- case Surface.ROTATION_180:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_180_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_180_enter);
- break;
- case Surface.ROTATION_270:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_minus_90_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_minus_90_enter);
- break;
+ final boolean customRotate = isCustomRotate();
+ if (customRotate) {
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mAnimHint == ROTATION_ANIMATION_JUMPCUT ? R.anim.rotation_animation_jump_exit
+ : R.anim.rotation_animation_xfade_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.rotation_animation_enter);
+ mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_alpha);
+ } else {
+ // Figure out how the screen has moved from the original rotation.
+ int delta = deltaRotation(mEndRotation, mStartRotation);
+ switch (delta) { /* Counter-Clockwise Rotations */
+ case Surface.ROTATION_0:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_0_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.rotation_animation_enter);
+ break;
+ case Surface.ROTATION_90:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_plus_90_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_plus_90_enter);
+ break;
+ case Surface.ROTATION_180:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_180_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_180_enter);
+ break;
+ case Surface.ROTATION_270:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_minus_90_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_minus_90_enter);
+ break;
+ }
}
mRotateExitAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
@@ -281,9 +299,20 @@ class ScreenRotationAnimation {
mRotateEnterAnimation.scaleCurrentDuration(animationScale);
mTransaction = mTransactionPool.acquire();
- startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
- startScreenshotRotationAnimation(animations, finishCallback, mainExecutor, animExecutor);
- //startColorAnimation(mTransaction, animationScale);
+ if (customRotate) {
+ mRotateAlphaAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
+ mRotateAlphaAnimation.restrictDuration(MAX_ANIMATION_DURATION);
+ mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
+
+ startScreenshotAlphaAnimation(animations, finishCallback, mainExecutor,
+ animExecutor);
+ startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
+ } else {
+ startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
+ startScreenshotRotationAnimation(animations, finishCallback, mainExecutor,
+ animExecutor);
+ //startColorAnimation(mTransaction, animationScale);
+ }
return true;
}
@@ -292,14 +321,24 @@ class ScreenRotationAnimation {
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
@NonNull ShellExecutor animExecutor) {
startSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
- mTransactionPool, mainExecutor, animExecutor, null /* position */);
+ mTransactionPool, mainExecutor, animExecutor, null /* position */,
+ 0 /* cornerRadius */, null /* clipRect */);
}
private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
@NonNull ShellExecutor animExecutor) {
startSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
- mTransactionPool, mainExecutor, animExecutor, null /* position */);
+ mTransactionPool, mainExecutor, animExecutor, null /* position */,
+ 0 /* cornerRadius */, null /* clipRect */);
+ }
+
+ private void startScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
+ @NonNull ShellExecutor animExecutor) {
+ startSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
+ mTransactionPool, mainExecutor, animExecutor, null /* position */,
+ 0 /* cornerRadius */, null /* clipRect */);
}
private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
@@ -349,13 +388,12 @@ class ScreenRotationAnimation {
t.remove(mScreenshotLayer);
}
mScreenshotLayer = null;
-
- if (mBackColorSurface != null) {
- if (mBackColorSurface.isValid()) {
- t.remove(mBackColorSurface);
- }
- mBackColorSurface = null;
+ }
+ if (mBackColorSurface != null) {
+ if (mBackColorSurface.isValid()) {
+ t.remove(mBackColorSurface);
}
+ mBackColorSurface = null;
}
t.apply();
mTransactionPool.release(t);
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 804e449decf8..86b73fc30ca8 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
@@ -33,6 +33,7 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -74,27 +75,33 @@ public class Transitions implements RemoteCallable<Transitions> {
public static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.debug.shell_transit", false);
- /** Transition type for dismissing split-screen via dragging the divider off the screen. */
- public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 1;
-
- /** Transition type for launching 2 tasks simultaneously. */
- public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2;
-
/** Transition type for exiting PIP via the Shell, via pressing the expand button. */
- public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3;
+ public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 1;
+
+ public static final int TRANSIT_EXIT_PIP_TO_SPLIT = TRANSIT_FIRST_CUSTOM + 2;
/** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */
- public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4;
+ public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 3;
+
+ /** Transition type for launching 2 tasks simultaneously. */
+ public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 4;
/** Transition type for entering split by opening an app into side-stage. */
public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
+ /** Transition type for dismissing split-screen via dragging the divider off the screen. */
+ public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 6;
+
+ /** Transition type for dismissing split-screen. */
+ public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 7;
+
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionPlayerImpl mPlayerImpl;
private final RemoteTransitionHandler mRemoteTransitionHandler;
+ private final DisplayController mDisplayController;
private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
@@ -117,15 +124,17 @@ public class Transitions implements RemoteCallable<Transitions> {
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
@NonNull DisplayController displayController, @NonNull Context context,
- @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
+ @NonNull ShellExecutor animExecutor) {
mOrganizer = organizer;
mContext = context;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mDisplayController = displayController;
mPlayerImpl = new TransitionPlayerImpl();
// The very last handler (0 in the list) should be the default one.
mHandlers.add(new DefaultTransitionHandler(displayController, pool, context, mainExecutor,
- animExecutor));
+ mainHandler, animExecutor));
// Next lowest priority is remote transitions.
mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
mHandlers.add(mRemoteTransitionHandler);
@@ -147,6 +156,7 @@ public class Transitions implements RemoteCallable<Transitions> {
mContext = null;
mMainExecutor = null;
mAnimExecutor = null;
+ mDisplayController = null;
mPlayerImpl = null;
mRemoteTransitionHandler = null;
}
@@ -351,6 +361,28 @@ public class Transitions implements RemoteCallable<Transitions> {
return;
}
+ // apply transfer starting window directly if there is no other task change.
+ final int changeSize = info.getChanges().size();
+ if (changeSize == 2) {
+ boolean nonTaskChange = true;
+ boolean transferStartingWindow = false;
+ for (int i = changeSize - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null) {
+ nonTaskChange = false;
+ break;
+ }
+ if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+ transferStartingWindow = true;
+ }
+ }
+ if (nonTaskChange && transferStartingWindow) {
+ t.apply();
+ onFinish(transitionToken, null /* wct */, null /* wctCB */);
+ return;
+ }
+ }
+
final ActiveTransition active = mActiveTransitions.get(activeIdx);
active.mInfo = info;
active.mStartT = t;
@@ -547,6 +579,17 @@ public class Transitions implements RemoteCallable<Transitions> {
break;
}
}
+ if (request.getDisplayChange() != null) {
+ TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
+ if (change.getEndRotation() != change.getStartRotation()) {
+ // Is a rotation, so dispatch to all displayChange listeners
+ if (wct == null) {
+ wct = new WindowContainerTransaction();
+ }
+ mDisplayController.getChangeController().dispatchOnRotateDisplay(wct,
+ change.getDisplayId(), change.getStartRotation(), change.getEndRotation());
+ }
+ }
active.mToken = mOrganizer.startTransition(
request.getType(), transitionToken, wct);
mActiveTransitions.add(active);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
index b9b671635010..7f8eaf1a9b55 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
@@ -18,14 +18,11 @@ package com.android.wm.shell.util;
import android.view.SurfaceControl;
-import java.util.ArrayList;
-
/**
* Utility class that takes care of counter-rotating surfaces during a transition animation.
*/
public class CounterRotator {
- SurfaceControl mSurface = null;
- ArrayList<SurfaceControl> mRotateChildren = null;
+ private SurfaceControl mSurface = null;
/** Gets the surface with the counter-rotation. */
public SurfaceControl getSurface() {
@@ -41,7 +38,6 @@ public class CounterRotator {
public void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta,
float displayW, float displayH) {
if (rotateDelta == 0) return;
- mRotateChildren = new ArrayList<>();
// We want to counter-rotate, so subtract from 4
rotateDelta = 4 - (rotateDelta + 4) % 4;
mSurface = new SurfaceControl.Builder()
@@ -64,24 +60,19 @@ public class CounterRotator {
}
/**
- * Add a surface that needs to be counter-rotate.
+ * Adds a surface that needs to be counter-rotate.
*/
public void addChild(SurfaceControl.Transaction t, SurfaceControl child) {
if (mSurface == null) return;
t.reparent(child, mSurface);
- mRotateChildren.add(child);
}
/**
- * Clean-up. This undoes any reparenting and effectively stops the counter-rotation.
+ * Clean-up. Since finishTransaction should reset all change leashes, we only need to remove the
+ * counter rotation surface.
*/
- public void cleanUp(SurfaceControl rootLeash) {
+ public void cleanUp(SurfaceControl.Transaction finishTransaction) {
if (mSurface == null) return;
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- for (int i = mRotateChildren.size() - 1; i >= 0; --i) {
- t.reparent(mRotateChildren.get(i), rootLeash);
- }
- t.remove(mSurface);
- t.apply();
+ finishTransaction.remove(mSurface);
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index ad4ccc0288ad..556742e2ac5f 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -16,10 +16,6 @@
<!-- restart launcher to activate TAPL -->
<option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
- <!-- reboot the device to teardown any crashed tests -->
- <option name="cleanup-action" value="REBOOT" />
- </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"/>
@@ -30,8 +26,16 @@
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6000s" />
<option name="hidden-api-checks" value="false" />
+ <option name="device-listeners"
+ value="com.android.server.wm.flicker.TraceFileReadyListener" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="(\w)+\.winscope" />
+ <option name="pull-pattern-keys" value="(\w)+\.mp4" />
+ <option name="collect-on-run-ended-only" value="false" />
+ <option name="clean-up" value="true" />
+ </metrics_collector>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="directory-keys" value="/sdcard/flicker" />
<option name="collect-on-run-ended-only" value="true" />
<option name="clean-up" value="true" />
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 c4be785cff19..cb478c84c2b7 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
@@ -17,11 +17,11 @@
@file:JvmName("CommonAssertions")
package com.android.wm.shell.flicker
-import android.graphics.Region
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
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
fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd {
@@ -118,10 +118,10 @@ fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- Region(0, 0, displayBounds.bounds.right,
+ Region.from(0, 0, displayBounds.bounds.right,
dividerRegion.bounds.top + WindowUtils.dockedStackDividerInset)
} else {
- Region(0, 0, dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
+ Region.from(0, 0, dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
displayBounds.bounds.bottom)
}
}
@@ -129,10 +129,10 @@ fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region {
fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- Region(0, dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
+ Region.from(0, dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.bounds.right, displayBounds.bounds.bottom)
} else {
- Region(dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, 0,
+ Region.from(dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, 0,
displayBounds.bounds.right, displayBounds.bounds.bottom)
}
} \ 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 b63d9fffdb61..4d87ec9e872f 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
@@ -43,4 +43,4 @@ fun <R> waitForResult(
} while (SystemClock.uptimeMillis() - startTime < timeout)
return (false to null)
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 038be9c190c2..b7c80df03ce2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -52,9 +52,9 @@ class AppPairsTestCannotPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
nonResizeableApp?.launchViaIntent(wmHelper)
// TODO pair apps through normal UX flow
@@ -80,7 +80,7 @@ class AppPairsTestCannotPairNonResizeableApps(
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest
+ @FlakyTest(bugId = 206753786)
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index bbc6b2dbece8..1ac664eb1f62 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -46,9 +46,9 @@ import org.junit.runners.Parameterized
class AppPairsTestPairPrimaryAndSecondaryApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
// TODO pair apps through normal UX flow
executeShellCommand(
@@ -65,7 +65,7 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest
+ @FlakyTest(bugId = 206753786)
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index bb784a809b7e..65eb9aa991c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -52,9 +52,9 @@ class AppPairsTestSupportPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
nonResizeableApp?.launchViaIntent(wmHelper)
// TODO pair apps through normal UX flow
@@ -84,7 +84,7 @@ class AppPairsTestSupportPairNonResizeableApps(
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest
+ @FlakyTest(bugId = 206753786)
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index a1a4db112dfd..12910dd74271 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -47,9 +47,9 @@ import org.junit.runners.Parameterized
class AppPairsTestUnpairPrimaryAndSecondaryApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
eachRun {
executeShellCommand(
@@ -69,7 +69,7 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest
+ @FlakyTest(bugId = 206753786)
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 9e20bbbc1a1b..863c3aff63a2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -25,14 +25,11 @@ 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.helpers.isRotated
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.repetitions
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -50,7 +47,6 @@ import org.junit.Test
abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected val context: Context = instrumentation.context
- protected val isRotated = testSpec.config.startRotation.isRotated()
protected val activityHelper = ActivityHelper.getInstance()
protected val appPairsHelper = AppPairsHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
@@ -82,20 +78,18 @@ abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
- transition(this, testSpec.config)
+ transition(this)
}
}
- internal open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
+ internal open val transition: FlickerBuilder.() -> Unit
+ get() = {
setup {
test {
device.wakeUpAndGoToHomeScreen()
}
eachRun {
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.startRotation)
primaryApp.launchViaIntent(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
nonResizeableApp?.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 56a2531a3fe1..f2f4877a44c4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -25,7 +25,6 @@ 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.endRotation
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
@@ -50,14 +49,14 @@ import org.junit.runners.Parameterized
class RotateTwoLaunchedAppsInAppPairsMode(
testSpec: FlickerTestParameter
) : RotateTwoLaunchedAppsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
executeShellCommand(composePairsCommand(
primaryTaskId, secondaryTaskId, true /* pair */))
waitAppsShown(primaryApp, secondaryApp)
- setRotation(testSpec.config.endRotation)
+ setRotation(testSpec.endRotation)
}
}
@@ -85,15 +84,19 @@ class RotateTwoLaunchedAppsInAppPairsMode(
@Presubmit
@Test
fun appPairsPrimaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
primaryApp.component)
- @FlakyTest
+ @Presubmit
@Test
fun appPairsSecondaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
secondaryApp.component)
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index 0699a4fd0512..2a173d16004f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -25,7 +25,6 @@ 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.endRotation
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
@@ -50,11 +49,11 @@ import org.junit.runners.Parameterized
class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
testSpec: FlickerTestParameter
) : RotateTwoLaunchedAppsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
- this.setRotation(testSpec.config.endRotation)
+ this.setRotation(testSpec.endRotation)
executeShellCommand(
composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
waitAppsShown(primaryApp, secondaryApp)
@@ -81,6 +80,10 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
@Test
override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@Presubmit
@Test
fun bothAppWindowsVisible() {
@@ -90,16 +93,16 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
}
}
- @FlakyTest(bugId = 172776659)
+ @Presubmit
@Test
fun appPairsPrimaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
primaryApp.component)
- @FlakyTest(bugId = 172776659)
+ @Presubmit
@Test
fun appPairsSecondaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
secondaryApp.component)
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
index b95193a17265..670fbd810907 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
@@ -34,7 +34,7 @@ abstract class RotateTwoLaunchedAppsTransition(
override val nonResizeableApp: SplitScreenHelper?
get() = null
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
test {
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 322d8b5e4dac..99f7e236ee3f 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
@@ -22,19 +22,17 @@ import android.app.NotificationManager
import android.content.Context
import android.os.ServiceManager
import android.view.Surface
-import androidx.test.filters.FlakyTest
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.helpers.SYSTEMUI_PACKAGE
-import com.android.server.wm.flicker.repetitions
import com.android.wm.shell.flicker.helpers.LaunchBubbleHelper
-import org.junit.Test
import org.junit.runners.Parameterized
/**
@@ -49,30 +47,23 @@ abstract class BaseBubbleScreen(protected val testSpec: FlickerTestParameter) {
protected val notifyManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE))
- protected val packageManager = context.getPackageManager()
- protected val uid = packageManager.getApplicationInfo(
+ protected val uid = context.packageManager.getApplicationInfo(
testApp.component.packageName, 0).uid
- protected lateinit var addBubbleBtn: UiObject2
- protected lateinit var cancelAllBtn: UiObject2
-
- protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ protected abstract val transition: FlickerBuilder.() -> Unit
@JvmOverloads
protected open fun buildTransition(
- extraSpec: FlickerBuilder.(Map<String, Any?>) -> Unit = {}
- ): FlickerBuilder.(Map<String, Any?>) -> Unit {
- return { configuration ->
-
+ extraSpec: FlickerBuilder.() -> Unit = {}
+ ): FlickerBuilder.() -> Unit {
+ return {
setup {
test {
notifyManager.setBubblesAllowed(testApp.component.packageName,
uid, NotificationManager.BUBBLE_PREFERENCE_ALL)
testApp.launchViaIntent(wmHelper)
- addBubbleBtn = device.wait(Until.findObject(
- By.text("Add Bubble")), FIND_OBJECT_TIMEOUT)
- cancelAllBtn = device.wait(Until.findObject(
- By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT)
+ waitAndGetAddBubbleBtn()
+ waitAndGetCancelAllBtn()
}
}
@@ -82,23 +73,19 @@ abstract class BaseBubbleScreen(protected val testSpec: FlickerTestParameter) {
testApp.exit()
}
- extraSpec(this, configuration)
+ extraSpec(this)
}
}
- @FlakyTest
- @Test
- fun testAppIsAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- }
- }
+ 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 {
- repeat { testSpec.config.repetitions }
- transition(this, testSpec.config)
+ transition(this)
}
}
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 bfdcb363a818..a928bbdb288f 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,6 +18,7 @@ package com.android.wm.shell.flicker.bubble
import android.content.Context
import android.graphics.Point
+import android.platform.test.annotations.Presubmit
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
@@ -28,6 +29,7 @@ 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 org.junit.Test
import org.junit.runners.Parameterized
/**
@@ -44,22 +46,31 @@ import org.junit.runners.Parameterized
@Group4
class DismissBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
- val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
- val displaySize = DisplayMetrics()
+ private val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ private val displaySize = DisplayMetrics()
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition() {
+ override val transition: FlickerBuilder.() -> Unit
+ get() = buildTransition {
setup {
eachRun {
- addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Add Bubble not found")
+ val addBubbleBtn = waitAndGetAddBubbleBtn()
+ addBubbleBtn?.click() ?: error("Add Bubble not found")
}
}
transitions {
- wm?.run { wm.getDefaultDisplay().getMetrics(displaySize) } ?: error("WM not found")
+ wm.run { wm.getDefaultDisplay().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)
showBubble?.run { drag(dist, 1000) } ?: error("Show bubble not found")
}
}
+
+ @Presubmit
+ @Test
+ fun testAppIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ }
+ }
}
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 42eeadf3ddd9..af629cc9f8ee 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.bubble
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
@@ -24,6 +25,7 @@ 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 org.junit.Test
import org.junit.runners.Parameterized
/**
@@ -42,18 +44,26 @@ import org.junit.runners.Parameterized
@Group4
class ExpandBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition() {
+ override val transition: FlickerBuilder.() -> Unit
+ get() = buildTransition {
setup {
test {
- addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Bubble widget 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)
showBubble?.run { showBubble.click() } ?: error("Bubble notify not found")
- device.pressBack()
}
}
+
+ @Presubmit
+ @Test
+ fun testAppIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ }
+ }
}
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
new file mode 100644
index 000000000000..61e27f21aa0f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.bubble
+
+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 org.junit.runner.RunWith
+import org.junit.Test
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a new activity from bubble.
+ *
+ * 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) {
+
+ 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()
+ }
+ }
+ transitions {
+ 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)
+ showBubble?.click() ?: error("Bubble notify not found")
+ instrumentation.uiAutomation.syncInputTransactions()
+ val cancelAllBtn = waitAndGetCancelAllBtn()
+ cancelAllBtn?.click() ?: error("Cancel widget not found")
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun testAppIsVisibleAtEnd() {
+ testSpec.assertLayersEnd {
+ this.isVisible(testApp.component)
+ }
+ }
+}
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 47e8c0c047a8..64636be1195f 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,14 @@
package com.android.wm.shell.flicker.bubble
+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.dsl.FlickerBuilder
import org.junit.runner.RunWith
+import org.junit.Test
import org.junit.runners.Parameterized
/**
@@ -39,10 +41,19 @@ import org.junit.runners.Parameterized
@Group4
class LaunchBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition() {
+ override val transition: FlickerBuilder.() -> Unit
+ get() = buildTransition {
transitions {
- addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Bubble widget not found")
+ val addBubbleBtn = waitAndGetAddBubbleBtn()
+ addBubbleBtn?.click() ?: error("Bubble widget not found")
}
}
+
+ @Presubmit
+ @Test
+ fun testAppIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ }
+ }
}
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 194e28fd6e8a..add11c10d75f 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
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.bubble
import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
@@ -25,6 +26,7 @@ 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 org.junit.Test
import org.junit.runners.Parameterized
/**
@@ -41,11 +43,12 @@ import org.junit.runners.Parameterized
@Group4
class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition() {
+ 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(
@@ -63,4 +66,12 @@ class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(test
}
}
}
+
+ @Presubmit
+ @Test
+ fun testAppIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ }
+ }
}
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
index 623055f659b9..cf4ea467a29b 100644
--- 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
@@ -17,25 +17,25 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.graphics.Region
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): android.graphics.Region {
- val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
+ 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): android.graphics.Region {
+ fun getSecondaryBounds(dividerBounds: Region): Region {
val displayBounds = WindowUtils.displayBounds
- val secondaryAppBounds = Region(0,
+ val secondaryAppBounds = Region.from(0,
dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight)
return secondaryAppBounds
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
index 2357b0debb33..7e232ea32181 100644
--- 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
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.graphics.Rect
import android.media.session.MediaController
import android.media.session.MediaSessionManager
import android.os.SystemClock
@@ -26,6 +25,7 @@ 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
@@ -66,7 +66,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
stringExtras: Map<String, String>
) {
super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
- wmHelper.waitFor("hasPipWindow") { it.wmState.hasPipWindow() }
+ wmHelper.waitPipShown()
}
private fun focusOnObject(selector: BySelector): Boolean {
@@ -88,7 +88,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
clickObject(ENTER_PIP_BUTTON_ID)
// Wait on WMHelper or simply wait for 3 seconds
- wmHelper?.waitFor("hasPipWindow") { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
+ 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
@@ -148,7 +148,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
}
// Wait for animation to complete.
- wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
+ wmHelper.waitPipGone()
wmHelper.waitForHomeActivityVisible()
}
@@ -165,7 +165,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
?: error("PIP window expand button not found")
val expandButtonBounds = expandPipObject.visibleBounds
uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY())
- wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
+ wmHelper.waitPipGone()
wmHelper.waitForAppTransitionIdle()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index bd44d082a1aa..c86a1229d8d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -28,7 +28,6 @@ import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
@@ -53,9 +52,9 @@ import org.junit.runners.Parameterized
class EnterSplitScreenDockActivity(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
device.launchSplitScreen(wmHelper)
}
@@ -69,7 +68,7 @@ class EnterSplitScreenDockActivity(
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
splitScreenApp.component)
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
index 625d48b8ab5a..2f9244be9c18 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
@@ -48,9 +48,9 @@ class EnterSplitScreenFromDetachedRecentTask(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
splitScreenApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index 2ed2806af528..1740c3ec24ca 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -27,7 +27,6 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
@@ -52,9 +51,9 @@ import org.junit.runners.Parameterized
class EnterSplitScreenLaunchToSide(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
device.launchSplitScreen(wmHelper)
device.reopenAppFromOverview(wmHelper)
@@ -69,13 +68,13 @@ class EnterSplitScreenLaunchToSide(
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
splitScreenApp.component)
@Presubmit
@Test
fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation,
secondaryApp.component)
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index ee6cf341c9ff..4c063b918e96 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -55,9 +55,9 @@ class EnterSplitScreenNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
nonResizeableApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index 163b6ffda6e2..f75dee619564 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -54,9 +54,9 @@ class EnterSplitScreenSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
nonResizeableApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index 2b629b0a7eb5..ef7d65e8a732 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -50,9 +49,9 @@ import org.junit.runners.Parameterized
class ExitLegacySplitScreenFromBottom(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
setup {
eachRun {
splitScreenApp.launchViaIntent(wmHelper)
@@ -74,7 +73,7 @@ class ExitLegacySplitScreenFromBottom(
splitScreenApp.component, secondaryApp.component,
FlickerComponentName.SNAPSHOT)
- @Postsubmit
+ @FlakyTest
@Test
fun layerBecomesInvisible() {
testSpec.assertLayers {
@@ -94,11 +93,11 @@ class ExitLegacySplitScreenFromBottom(
}
}
- @Postsubmit
+ @FlakyTest
@Test
fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
- @Postsubmit
+ @FlakyTest
@Test
fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index 95fe3bef4852..d913a6d85d3d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -51,9 +51,9 @@ import org.junit.runners.Parameterized
class ExitPrimarySplitScreenShowSecondaryFullscreen(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
teardown {
eachRun {
secondaryApp.exit(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index f7d628d48769..f3ff7b156aaf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -53,9 +53,9 @@ class LegacySplitScreenFromIntentNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
splitScreenApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index a5c6571f68de..42e707ab0850 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -53,9 +53,9 @@ class LegacySplitScreenFromIntentSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
splitScreenApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 6f486b0ddfea..c1fba7d1530c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,9 +16,9 @@
package com.android.wm.shell.flicker.legacysplitscreen
-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
@@ -55,9 +55,9 @@ class LegacySplitScreenFromRecentNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
nonResizeableApp.launchViaIntent(wmHelper)
@@ -124,7 +124,7 @@ class LegacySplitScreenFromRecentNotSupportNonResizable(
}
}
- @Postsubmit
+ @FlakyTest
@Test
fun nonResizableAppWindowBecomesVisible() {
testSpec.assertWm {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index f03c927b8d58..6ac8683ac054 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -54,9 +54,9 @@ class LegacySplitScreenFromRecentSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
nonResizeableApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
index 1e89a25c06df..b01f41c9e2ec 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
@@ -26,7 +26,7 @@ import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
abstract class LegacySplitScreenRotateTransition(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 2ccd03bf1d6a..fb1004bda0cb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -16,16 +16,15 @@
package com.android.wm.shell.flicker.legacysplitscreen
-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.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.exitSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
@@ -61,8 +60,8 @@ class LegacySplitScreenToLauncher(
) : LegacySplitScreenTransition(testSpec) {
private val testApp = SimpleAppHelper(instrumentation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
setup {
test {
device.wakeUpAndGoToHomeScreen()
@@ -70,7 +69,7 @@ class LegacySplitScreenToLauncher(
}
eachRun {
testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.endRotation)
+ this.setRotation(testSpec.endRotation)
device.launchSplitScreen(wmHelper)
device.waitForIdle()
}
@@ -109,7 +108,7 @@ class LegacySplitScreenToLauncher(
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@@ -117,11 +116,11 @@ class LegacySplitScreenToLauncher(
@Test
fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
- @Postsubmit
+ @FlakyTest
@Test
fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible()
- @Postsubmit
+ @FlakyTest
@Test
fun layerBecomesInvisible() {
testSpec.assertLayers {
@@ -131,7 +130,7 @@ class LegacySplitScreenToLauncher(
}
}
- @Postsubmit
+ @FlakyTest
@Test
fun focusDoesNotChange() {
testSpec.assertEventLog {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 661c8b69068e..a4a1f617e497 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -25,12 +25,9 @@ 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.helpers.isRotated
import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
@@ -45,7 +42,6 @@ import org.junit.Test
abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected val context: Context = instrumentation.context
- protected val isRotated = testSpec.config.startRotation.isRotated()
protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
@@ -82,15 +78,15 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa
FlickerComponentName.SPLASH_SCREEN,
FlickerComponentName.SNAPSHOT)
- protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
+ protected open val transition: FlickerBuilder.() -> Unit
+ get() = {
setup {
eachRun {
device.wakeUpAndGoToHomeScreen()
device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
splitScreenApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
@@ -105,19 +101,17 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
- transition(this, testSpec.config)
+ transition(this)
}
}
- internal open val cleanSetup: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
+ internal open val cleanSetup: FlickerBuilder.() -> Unit
+ get() = {
setup {
eachRun {
device.wakeUpAndGoToHomeScreen()
device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index 34eff80a04bc..087b21c544c5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -49,9 +49,9 @@ import org.junit.runners.Parameterized
class OpenAppToLegacySplitScreen(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
device.launchSplitScreen(wmHelper)
wmHelper.waitForAppTransitionIdle()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 58e1def6f37a..a510d699387e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.graphics.Region
import android.util.Rational
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -37,10 +36,10 @@ 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.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.region.Region
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
@@ -69,12 +68,12 @@ class ResizeLegacySplitScreen(
private val testAppTop = SimpleAppHelper(instrumentation)
private val testAppBottom = ImeAppHelper(instrumentation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
setup {
eachRun {
device.wakeUpAndGoToHomeScreen()
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.startRotation)
this.launcherStrategy.clearRecentAppsFromOverview()
testAppBottom.launchViaIntent(wmHelper)
device.pressHome()
@@ -134,6 +133,7 @@ class ResizeLegacySplitScreen(
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@@ -166,9 +166,9 @@ class ResizeLegacySplitScreen(
val dividerBounds =
layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
- val topAppBounds = Region(0, 0, dividerBounds.right,
+ val topAppBounds = Region.from(0, 0, dividerBounds.right,
dividerBounds.top + WindowUtils.dockedStackDividerInset)
- val bottomAppBounds = Region(0,
+ val bottomAppBounds = Region.from(0,
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
@@ -187,9 +187,9 @@ class ResizeLegacySplitScreen(
val dividerBounds =
layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
- val topAppBounds = Region(0, 0, dividerBounds.right,
+ val topAppBounds = Region.from(0, 0, dividerBounds.right,
dividerBounds.top + WindowUtils.dockedStackDividerInset)
- val bottomAppBounds = Region(0,
+ val bottomAppBounds = Region.from(0,
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
@@ -220,8 +220,8 @@ class ResizeLegacySplitScreen(
.map {
val description = (startRatio.toString().replace("/", "-") + "_to_" +
stopRatio.toString().replace("/", "-"))
- val newName = "${FlickerTestParameter.defaultName(it.config)}_$description"
- FlickerTestParameter(it.config, name = newName)
+ val newName = "${FlickerTestParameter.defaultName(it)}_$description"
+ FlickerTestParameter(it.config, nameOverride = newName)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 8a50bc0b20cf..d703ea082c87 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -29,7 +29,6 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
@@ -53,12 +52,12 @@ import org.junit.runners.Parameterized
class RotateOneLaunchedAppAndEnterSplitScreen(
testSpec: FlickerTestParameter
) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
device.launchSplitScreen(wmHelper)
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
@@ -69,14 +68,14 @@ class RotateOneLaunchedAppAndEnterSplitScreen(
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
splitScreenApp.component)
@Presubmit
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 84676a9186be..6b1883914e59 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -29,7 +29,6 @@ import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
@@ -53,11 +52,11 @@ import org.junit.runners.Parameterized
class RotateOneLaunchedAppInSplitScreenMode(
testSpec: FlickerTestParameter
) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
device.launchSplitScreen(wmHelper)
}
}
@@ -69,13 +68,13 @@ class RotateOneLaunchedAppInSplitScreenMode(
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() = testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(
- testSpec.config.startRotation, splitScreenApp.component)
+ testSpec.startRotation, splitScreenApp.component)
@Presubmit
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 2abdca9216f9..acd658b5ba56 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.legacysplitscreen
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
@@ -29,7 +30,6 @@ import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
@@ -54,11 +54,11 @@ import org.junit.runners.Parameterized
class RotateTwoLaunchedAppAndEnterSplitScreen(
testSpec: FlickerTestParameter
) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
device.launchSplitScreen(wmHelper)
device.reopenAppFromOverview(wmHelper)
}
@@ -71,20 +71,20 @@ class RotateTwoLaunchedAppAndEnterSplitScreen(
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
splitScreenApp.component)
@Presubmit
@Test
fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation,
secondaryApp.component)
@Presubmit
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index fe9b9f514015..b40be8b5f401 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -30,7 +30,6 @@ import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
@@ -55,18 +54,18 @@ import org.junit.runners.Parameterized
class RotateTwoLaunchedAppInSplitScreenMode(
testSpec: FlickerTestParameter
) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
setup {
eachRun {
device.launchSplitScreen(wmHelper)
device.reopenAppFromOverview(wmHelper)
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
transitions {
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
@@ -77,20 +76,20 @@ class RotateTwoLaunchedAppInSplitScreenMode(
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
splitScreenApp.component)
@Presubmit
@Test
fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation,
secondaryApp.component)
@Presubmit
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
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 52a744f3897d..db94de238a5d 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
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -26,7 +27,9 @@ 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.rules.WMFlickerServiceRuleForTestSpec
import org.junit.FixMethodOrder
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -55,16 +58,42 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+ @get:Rule
+ val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
+
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
transitions {
pipApp.clickEnterPipButton(wmHelper)
}
}
+ @FlakyTest
+ @Test
+ fun runPresubmitAssertion() {
+ flickerRule.checkPresubmitAssertions()
+ }
+
+ @FlakyTest
+ @Test
+ fun runPostsubmitAssertion() {
+ flickerRule.checkPostsubmitAssertions()
+ }
+
+ @FlakyTest
+ @Test
+ fun runFlakyAssertion() {
+ flickerRule.checkFlakyAssertions()
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
/**
* Checks [pipApp] window remains visible throughout the animation
*/
@@ -94,8 +123,8 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Presubmit
@Test
fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -106,8 +135,8 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@Presubmit
@Test
fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -153,13 +182,14 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
}
/**
- * Checks the focus doesn't change during the animation
+ * Checks that the focus changes between the [pipApp] window and the launcher when
+ * closing the pip window
*/
- @FlakyTest
+ @Postsubmit
@Test
- fun focusDoesNotChange() {
+ fun focusChanges() {
testSpec.assertEventLog {
- this.focusDoesNotChange()
+ this.focusChanges(pipApp.`package`, "NexusLauncherActivity")
}
}
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 c8c3f4d64294..afe64e3d4abc 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
@@ -74,9 +74,9 @@ class EnterPipToOtherOrientationTest(
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- setupAndTeardown(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setupAndTeardown(this)
setup {
eachRun {
@@ -98,7 +98,7 @@ class EnterPipToOtherOrientationTest(
// Enter PiP, and assert that the PiP is within bounds now that the device is back
// in portrait
broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
- wmHelper.waitFor { it.wmState.hasPipWindow() }
+ wmHelper.waitPipShown()
wmHelper.waitForAppTransitionIdle()
// during rotation the status bar becomes invisible and reappears at the end
wmHelper.waitForNavBarStatusBarVisible()
@@ -117,7 +117,7 @@ class EnterPipToOtherOrientationTest(
* Checks that the [FlickerComponentName.STATUS_BAR] has the correct position at
* the start and end of the transition
*/
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
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 64b7eb53bd6f..990872f58dc1 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
@@ -34,8 +34,8 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans
@Presubmit
@Test
open fun pipAppWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -46,8 +46,8 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans
@Presubmit
@Test
open fun pipAppLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -102,7 +102,7 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans
}
/**
- * Checks that the visible region of [pipApp] covers the full display area at the end of
+ * Checks that the visible region oft [pipApp] covers the full display area at the end of
* the transition
*/
@Presubmit
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 5207fed59208..173140d77cb6 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
@@ -16,25 +16,25 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
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.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import org.junit.Test
/**
* Base class for exiting pip (closing pip window) without returning to the app
*/
abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition(eachRun = true) { configuration ->
+ override val transition: FlickerBuilder.() -> Unit
+ get() = buildTransition(eachRun = true) {
setup {
eachRun {
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
@@ -52,11 +52,28 @@ abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition
@Presubmit
@Test
open fun pipWindowBecomesInvisible() {
- testSpec.assertWm {
- this.invoke("hasPipWindow") {
- it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component)
- }.then().invoke("!hasPipWindow") {
- it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component)
+ if (isShellTransitionsEnabled) {
+ // 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 {
+ this.invoke("hasPipWindow") {
+ it.isPinned(pipApp.component)
+ .isAppWindowVisible(pipApp.component)
+ .isAppWindowOnTop(pipApp.component)
+ }.then().invoke("!hasPipWindow") {
+ it.isNotPinned(pipApp.component)
+ .isAppWindowNotOnTop(pipApp.component)
+ }
+ }
+ testSpec.assertWmEnd { isAppWindowInvisible(pipApp.component) }
+ } else {
+ testSpec.assertWm {
+ this.invoke("hasPipWindow") {
+ it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component)
+ }.then().invoke("!hasPipWindow") {
+ it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component)
+ }
}
}
}
@@ -79,14 +96,13 @@ abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition
}
/**
- * Checks that the focus changes between the [pipApp] window and the launcher when
- * closing the pip window
+ * Checks that the focus doesn't change between windows during the transition
*/
- @FlakyTest(bugId = 151179149)
+ @Postsubmit
@Test
- open fun focusChanges() {
+ open fun focusDoesNotChange() {
testSpec.assertEventLog {
- this.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+ this.focusDoesNotChange()
}
}
} \ No newline at end of file
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 b53342d6f2f7..3a9a0705908d 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
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.pip
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
@@ -24,6 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -59,7 +61,7 @@ class ExitPipViaExpandButtonClickTest(
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true) {
setup {
eachRun {
@@ -75,6 +77,16 @@ class ExitPipViaExpandButtonClickTest(
}
}
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 197726610)
+ @Test
+ override fun pipLayerExpands() = super.pipLayerExpands()
+
companion object {
/**
* Creates the test configurations.
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 1fec3cf85214..03c8929f9919 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
@@ -17,13 +17,17 @@
package com.android.wm.shell.flicker.pip
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.rules.WMFlickerServiceRuleForTestSpec
import org.junit.FixMethodOrder
+import org.junit.Rule
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -52,11 +56,13 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransition(testSpec) {
+ @get:Rule
+ val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true) {
setup {
eachRun {
@@ -72,6 +78,34 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit
}
}
+ @FlakyTest
+ @Test
+ fun runPresubmitAssertion() {
+ flickerRule.checkPresubmitAssertions()
+ }
+
+ @FlakyTest
+ @Test
+ fun runPostsubmitAssertion() {
+ flickerRule.checkPostsubmitAssertions()
+ }
+
+ @FlakyTest
+ @Test
+ fun runFlakyAssertion() {
+ flickerRule.checkFlakyAssertions()
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 197726610)
+ @Test
+ override fun pipLayerExpands() = super.pipLayerExpands()
+
companion object {
/**
* Creates the test configurations.
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 73626c23065a..976b7c6980a1 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,13 +17,17 @@
package com.android.wm.shell.flicker.pip
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.rules.WMFlickerServiceRuleForTestSpec
import org.junit.FixMethodOrder
+import org.junit.Rule
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -52,14 +56,45 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ @get:Rule
+ val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
+
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
pipApp.closePipWindow(wmHelper)
}
}
+ @FlakyTest
+ @Test
+ fun runPresubmitAssertion() {
+ flickerRule.checkPresubmitAssertions()
+ }
+
+ @FlakyTest
+ @Test
+ fun runPostsubmitAssertion() {
+ flickerRule.checkPostsubmitAssertions()
+ }
+
+ @FlakyTest
+ @Test
+ fun runFlakyAssertion() {
+ flickerRule.checkFlakyAssertions()
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 215869110)
+ @Test
+ override fun focusDoesNotChange() = super.focusDoesNotChange()
+
companion object {
/**
* Creates the test configurations.
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 9e43deef8d99..906123914731 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
@@ -16,7 +16,6 @@
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
@@ -55,37 +54,21 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { args ->
- super.transition(this, args)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
val pipRegion = wmHelper.getWindowRegion(pipApp.component).bounds
val pipCenterX = pipRegion.centerX()
val pipCenterY = pipRegion.centerY()
val displayCenterX = device.displayWidth / 2
device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 10)
- wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
+ wmHelper.waitPipGone()
wmHelper.waitForWindowSurfaceDisappeared(pipApp.component)
wmHelper.waitForAppTransitionIdle()
}
}
- @Presubmit
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
-
- @Presubmit
- @Test
- override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
-
@FlakyTest
@Test
override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
@@ -94,18 +77,10 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti
@Test
override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
- @Presubmit
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
-
- @Presubmit
- @Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
-
companion object {
/**
* Creates the test configurations.
@@ -118,7 +93,7 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 20)
+ repetitions = 5)
}
}
} \ No newline at end of file
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 d0fee9a82093..8d14f70357b1 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,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -55,7 +56,7 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true) {
transitions {
pipApp.doubleClickPipWindow(wmHelper)
@@ -69,8 +70,8 @@ class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition
@Presubmit
@Test
fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -81,8 +82,8 @@ class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition
@Presubmit
@Test
fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -148,7 +149,7 @@ class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition
/**
* Checks that the focus doesn't change between windows during the transition
*/
- @FlakyTest
+ @Postsubmit
@Test
fun focusDoesNotChange() {
testSpec.assertEventLog {
@@ -156,6 +157,10 @@ class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition
}
}
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
companion object {
/**
* Creates the test configurations.
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 0ab857d755ee..e4150241d42c 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
@@ -17,14 +17,16 @@
package com.android.wm.shell.flicker.pip
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.traces.RegionSubject
+import com.android.server.wm.flicker.traces.region.RegionSubject
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -59,7 +61,7 @@ class MovePipDownShelfHeightChangeTest(
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = false) {
teardown {
eachRun {
@@ -78,6 +80,11 @@ class MovePipDownShelfHeightChangeTest(
current.isHigherOrEqual(previous.region)
}
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
companion object {
/**
* Creates the test configurations.
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 6e0324c17272..0499e7de9a0a 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
@@ -19,7 +19,7 @@ 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.traces.RegionSubject
+import com.android.server.wm.flicker.traces.region.RegionSubject
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
@@ -66,8 +66,8 @@ abstract class MovePipShelfHeightTransition(
@Presubmit
@Test
open fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -78,8 +78,8 @@ abstract class MovePipShelfHeightTransition(
@Presubmit
@Test
open fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
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 e507edfda48c..4a4c46c596a2 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
@@ -17,14 +17,16 @@
package com.android.wm.shell.flicker.pip
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.traces.RegionSubject
+import com.android.server.wm.flicker.traces.region.RegionSubject
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -59,7 +61,7 @@ class MovePipUpShelfHeightChangeTest(
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = false) {
teardown {
eachRun {
@@ -78,6 +80,11 @@ class MovePipUpShelfHeightChangeTest(
current.isLowerOrEqual(previous.region)
}
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
companion object {
/**
* Creates the test configurations.
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 aba8aced298f..1d61ab48d663 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
@@ -18,6 +18,7 @@ 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
@@ -25,10 +26,12 @@ 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.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.ImeAppHelper
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -47,12 +50,12 @@ import org.junit.runners.Parameterized
class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition(eachRun = false) { configuration ->
+ override val transition: FlickerBuilder.() -> Unit
+ get() = buildTransition(eachRun = false) {
setup {
test {
imeApp.launchViaIntent(wmHelper)
- setRotation(configuration.startRotation)
+ setRotation(testSpec.startRotation)
}
}
teardown {
@@ -71,15 +74,31 @@ class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
}
}
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() {
+ // This test doesn't work in shell transitions because of b/206753786
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
+ @FlakyTest(bugId = 214452854)
+ @Test
+ fun statusBarLayerRotatesScales_shellTransit() {
+ assumeTrue(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
/**
* Ensure the pip window remains visible throughout any keyboard interactions
*/
@Presubmit
@Test
fun pipInVisibleBounds() {
- testSpec.assertWm {
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
+ coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 9bea5c03dadb..21175a0767a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -27,7 +27,6 @@ import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.FixedAppHelper
@@ -64,10 +63,8 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
assumeFalse(isShellTransitionsEnabled())
}
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
test {
removeAllTasksButHome()
@@ -92,11 +89,16 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
}
}
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
@FlakyTest(bugId = 161435597)
@Test
fun pipWindowInsideDisplayBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -113,8 +115,8 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
@FlakyTest(bugId = 161435597)
@Test
fun pipLayerInsideDisplayBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
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 669f37ad1e72..b2b50ade78a9 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
@@ -25,12 +25,10 @@ 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.endRotation
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.FixMethodOrder
@@ -47,7 +45,7 @@ import org.junit.runners.Parameterized
* Actions:
* Launch a [pipApp] in pip mode
* Launch another app [fixedApp] (appears below pip)
- * Rotate the screen from [testSpec.config.startRotation] to [testSpec.config.endRotation]
+ * Rotate the screen from [testSpec.startRotation] to [testSpec.endRotation]
* (usually, 0->90 and 90->0)
*
* Notes:
@@ -65,21 +63,21 @@ import org.junit.runners.Parameterized
@Group4
class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val fixedApp = FixedAppHelper(instrumentation)
- private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- private val screenBoundsEnd = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
+ private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.startRotation)
+ private val screenBoundsEnd = WindowUtils.getDisplayBounds(testSpec.endRotation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition(eachRun = false) { configuration ->
+ override val transition: FlickerBuilder.() -> Unit
+ get() = buildTransition(eachRun = false) {
setup {
test {
fixedApp.launchViaIntent(wmHelper)
}
eachRun {
- setRotation(configuration.startRotation)
+ setRotation(testSpec.startRotation)
}
}
transitions {
- setRotation(configuration.endRotation)
+ setRotation(testSpec.endRotation)
}
}
@@ -100,7 +98,7 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
/**
* Checks the position of the status bar at the start and end of the transition
*/
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
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 e8a61e8a1dae..bb66f7bbc01f 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
@@ -26,15 +26,12 @@ 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.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isRotated
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.repetitions
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -44,11 +41,10 @@ import org.junit.Test
abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- protected val isRotated = testSpec.config.startRotation.isRotated()
protected val pipApp = PipAppHelper(instrumentation)
- protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
- protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ 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 {
@@ -81,16 +77,14 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
- transition(this, testSpec.config)
+ transition(this)
}
}
/**
* Gets a configuration that handles basic setup and teardown of pip tests
*/
- protected val setupAndTeardown: FlickerBuilder.(Map<String, Any?>) -> Unit
+ protected val setupAndTeardown: FlickerBuilder.() -> Unit
get() = {
setup {
test {
@@ -121,23 +115,23 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
protected open fun buildTransition(
eachRun: Boolean,
stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"),
- extraSpec: FlickerBuilder.(Map<String, Any?>) -> Unit = {}
- ): FlickerBuilder.(Map<String, Any?>) -> Unit {
- return { configuration ->
- setupAndTeardown(this, configuration)
+ extraSpec: FlickerBuilder.() -> Unit = {}
+ ): FlickerBuilder.() -> Unit {
+ return {
+ setupAndTeardown(this)
setup {
test {
removeAllTasksButHome()
if (!eachRun) {
pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
- wmHelper.waitFor { it.wmState.hasPipWindow() }
+ wmHelper.waitPipShown()
}
}
eachRun {
if (eachRun) {
pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
- wmHelper.waitFor { it.wmState.hasPipWindow() }
+ wmHelper.waitPipShown()
}
}
}
@@ -155,7 +149,7 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
}
}
- extraSpec(this, configuration)
+ extraSpec(this)
}
}
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 d6dbc366aec0..8dd91048d29b 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,6 +16,7 @@
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
@@ -28,7 +29,6 @@ import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
-import org.junit.Assert.assertEquals
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,7 +37,7 @@ import org.junit.runners.Parameterized
/**
* Test Pip with orientation changes.
- * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ * To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -50,9 +50,9 @@ class SetRequestedOrientationWhilePinnedTest(
private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- setupAndTeardown(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setupAndTeardown(this)
setup {
eachRun {
@@ -76,35 +76,34 @@ class SetRequestedOrientationWhilePinnedTest(
pipApp.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(pipApp.component)
wmHelper.waitForRotation(Surface.ROTATION_90)
- assertEquals(Surface.ROTATION_90, device.displayRotation)
}
}
- @FlakyTest
+ @Presubmit
@Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+ fun displayEndsAt90Degrees() {
+ testSpec.assertWmEnd {
+ hasRotation(Surface.ROTATION_90)
+ }
+ }
- @FlakyTest
+ @Presubmit
@Test
- override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
- @FlakyTest
+ @Presubmit
@Test
override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@FlakyTest
@Test
- override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
-
- @FlakyTest
- @Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest
+ @FlakyTest(bugId = 206753786)
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
- @FlakyTest
+ @Presubmit
@Test
fun pipWindowInsideDisplay() {
testSpec.assertWmStart {
@@ -112,7 +111,7 @@ class SetRequestedOrientationWhilePinnedTest(
}
}
- @FlakyTest
+ @Presubmit
@Test
fun pipAppShowsOnTop() {
testSpec.assertWmEnd {
@@ -120,7 +119,7 @@ class SetRequestedOrientationWhilePinnedTest(
}
}
- @FlakyTest
+ @Presubmit
@Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
@@ -128,13 +127,15 @@ class SetRequestedOrientationWhilePinnedTest(
}
}
- @FlakyTest
+ @Presubmit
@Test
- fun pipAlwaysVisible() = testSpec.assertWm {
- this.isAppWindowVisible(pipApp.component)
+ fun pipAlwaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(pipApp.component)
+ }
}
- @FlakyTest
+ @Presubmit
@Test
fun pipAppLayerCoversFullScreen() {
testSpec.assertLayersEnd {
@@ -142,10 +143,6 @@ class SetRequestedOrientationWhilePinnedTest(
}
}
- @FlakyTest
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
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 31e9167c79b2..9c3b0fa183b6 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
@@ -27,7 +27,7 @@ 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
+import org.junit.Assume.assumeTrue
import org.junit.Before
abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATION_0) {
@@ -37,7 +37,7 @@ abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATIO
@Before
final override fun televisionSetUp() {
// Should run only on TVs.
- Assume.assumeTrue(isTelevision)
+ assumeTrue(isTelevision)
systemUiProcessObserver.start()
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
index 2cdbffa7589c..f40aa66932cd 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -25,6 +25,7 @@
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:launchMode="singleTop"
+ android:theme="@style/CutoutShortEdges"
android:label="FixedApp"
android:exported="true">
<intent-filter>
@@ -37,6 +38,7 @@
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">
@@ -52,6 +54,7 @@
<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">
@@ -68,6 +71,7 @@
<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>
@@ -79,6 +83,7 @@
<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>
@@ -90,6 +95,7 @@
<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>
@@ -100,6 +106,7 @@
<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>
@@ -111,6 +118,7 @@
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" />
@@ -121,6 +129,7 @@
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/values/styles.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
new file mode 100644
index 000000000000..87a61a88c094
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?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="CutoutDefault">
+ <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+ </style>
+
+ <style name="CutoutShortEdges">
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ </style>
+
+ <style name="CutoutNever">
+ <item name="android:windowLayoutInDisplayCutoutMode">never</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index a3b98a8fc880..a6caefe6d3e7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -37,6 +37,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
import android.content.Context;
import android.content.LocusId;
import android.content.pm.ParceledListSlice;
@@ -334,8 +335,7 @@ public class ShellTaskOrganizerTests {
mOrganizer.onTaskAppeared(taskInfo1, null);
// sizeCompatActivity is null if top activity is not in size compat.
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
// sizeCompatActivity is non-null if top activity is in size compat.
clearInvocations(mCompatUI);
@@ -345,8 +345,7 @@ public class ShellTaskOrganizerTests {
taskInfo2.topActivityInSizeCompat = true;
taskInfo2.isVisible = true;
mOrganizer.onTaskInfoChanged(taskInfo2);
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- taskInfo1.configuration, taskListener);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener);
// Not show size compat UI if task is not visible.
clearInvocations(mCompatUI);
@@ -356,13 +355,121 @@ public class ShellTaskOrganizerTests {
taskInfo3.topActivityInSizeCompat = true;
taskInfo3.isVisible = false;
mOrganizer.onTaskInfoChanged(taskInfo3);
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo3, null /* taskListener */);
clearInvocations(mCompatUI);
mOrganizer.onTaskVanished(taskInfo1);
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+ }
+
+ @Test
+ public void testOnEligibleForLetterboxEducationActivityChanged() {
+ final RunningTaskInfo taskInfo1 = createTaskInfo(12, WINDOWING_MODE_FULLSCREEN);
+ taskInfo1.displayId = DEFAULT_DISPLAY;
+ taskInfo1.topActivityEligibleForLetterboxEducation = false;
+ final TrackingTaskListener taskListener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+
+ // Task listener sent to compat UI is null if top activity isn't eligible for letterbox
+ // education.
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+
+ // Task listener is non-null if top activity is eligible for letterbox education and task
+ // is visible.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo2 =
+ createTaskInfo(taskInfo1.taskId, WINDOWING_MODE_FULLSCREEN);
+ taskInfo2.displayId = taskInfo1.displayId;
+ taskInfo2.topActivityEligibleForLetterboxEducation = true;
+ taskInfo2.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo2);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener);
+
+ // Task listener is null if task is invisible.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo3 =
+ createTaskInfo(taskInfo1.taskId, WINDOWING_MODE_FULLSCREEN);
+ taskInfo3.displayId = taskInfo1.displayId;
+ taskInfo3.topActivityEligibleForLetterboxEducation = true;
+ taskInfo3.isVisible = false;
+ mOrganizer.onTaskInfoChanged(taskInfo3);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo3, null /* taskListener */);
+
+ clearInvocations(mCompatUI);
+ mOrganizer.onTaskVanished(taskInfo1);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+ }
+
+ @Test
+ public void testOnCameraCompatActivityChanged() {
+ final RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN);
+ taskInfo1.displayId = DEFAULT_DISPLAY;
+ taskInfo1.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+ final TrackingTaskListener taskListener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+
+ // Task listener sent to compat UI is null if top activity doesn't request a camera
+ // compat control.
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+
+ // Task listener is non-null when request a camera compat control for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo2 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo2.displayId = taskInfo1.displayId;
+ taskInfo2.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ taskInfo2.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo2);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener);
+
+ // CompatUIController#onCompatInfoChanged is called when requested state for a camera
+ // compat control changes for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo3 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo3.displayId = taskInfo1.displayId;
+ taskInfo3.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+ taskInfo3.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo3);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo3, taskListener);
+
+ // CompatUIController#onCompatInfoChanged is called when a top activity goes in size compat
+ // mode for a visible task that has a compat control.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo4 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo4.displayId = taskInfo1.displayId;
+ taskInfo4.topActivityInSizeCompat = true;
+ taskInfo4.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+ taskInfo4.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo4);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo4, taskListener);
+
+ // Task linster is null when a camera compat control is dimissed for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo5 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo5.displayId = taskInfo1.displayId;
+ taskInfo5.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+ taskInfo5.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo5);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo5, null /* taskListener */);
+
+ // Task linster is null when request a camera compat control for a invisible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo6 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo6.displayId = taskInfo1.displayId;
+ taskInfo6.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ taskInfo6.isVisible = false;
+ mOrganizer.onTaskInfoChanged(taskInfo6);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo6, null /* taskListener */);
+
+ clearInvocations(mCompatUI);
+ mOrganizer.onTaskVanished(taskInfo1);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index 1cbad155ba7b..03df92fd8477 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -21,6 +21,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -43,12 +45,14 @@ import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
+import com.android.wm.shell.transition.Transitions;
import org.junit.After;
import org.junit.Before;
@@ -75,6 +79,8 @@ public class TaskViewTest extends ShellTestCase {
HandlerExecutor mExecutor;
@Mock
SyncTransactionQueue mSyncQueue;
+ @Mock
+ TaskViewTransitions mTaskViewTransitions;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -110,7 +116,7 @@ public class TaskViewTest extends ShellTestCase {
return null;
}).when(mSyncQueue).runInSync(any());
- mTaskView = new TaskView(mContext, mOrganizer, mSyncQueue);
+ mTaskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
mTaskView.setListener(mExecutor, mViewListener);
}
@@ -123,7 +129,7 @@ public class TaskViewTest extends ShellTestCase {
@Test
public void testSetPendingListener_throwsException() {
- TaskView taskView = new TaskView(mContext, mOrganizer, mSyncQueue);
+ TaskView taskView = new TaskView(mContext, mOrganizer, mTaskViewTransitions, mSyncQueue);
taskView.setListener(mExecutor, mViewListener);
try {
taskView.setListener(mExecutor, mViewListener);
@@ -144,7 +150,8 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
- public void testOnTaskAppeared_noSurface() {
+ public void testOnTaskAppeared_noSurface_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
@@ -154,7 +161,8 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
- public void testOnTaskAppeared_withSurface() {
+ public void testOnTaskAppeared_withSurface_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
@@ -163,7 +171,8 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
- public void testSurfaceCreated_noTask() {
+ public void testSurfaceCreated_noTask_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
verify(mViewListener).onInitialized();
@@ -172,7 +181,8 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
- public void testSurfaceCreated_withTask() {
+ public void testSurfaceCreated_withTask_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
@@ -181,7 +191,8 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
- public void testSurfaceDestroyed_noTask() {
+ public void testSurfaceDestroyed_noTask_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
SurfaceHolder sh = mock(SurfaceHolder.class);
mTaskView.surfaceCreated(sh);
mTaskView.surfaceDestroyed(sh);
@@ -190,7 +201,8 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
- public void testSurfaceDestroyed_withTask() {
+ public void testSurfaceDestroyed_withTask_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
SurfaceHolder sh = mock(SurfaceHolder.class);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(sh);
@@ -201,7 +213,8 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
- public void testOnReleased() {
+ public void testOnReleased_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
mTaskView.release();
@@ -211,7 +224,8 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
- public void testOnTaskVanished() {
+ public void testOnTaskVanished_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.surfaceCreated(mock(SurfaceHolder.class));
mTaskView.onTaskVanished(mTaskInfo);
@@ -220,7 +234,8 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
- public void testOnBackPressedOnTaskRoot() {
+ public void testOnBackPressedOnTaskRoot_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
@@ -228,17 +243,158 @@ public class TaskViewTest extends ShellTestCase {
}
@Test
- public void testSetOnBackPressedOnTaskRoot() {
+ public void testSetOnBackPressedOnTaskRoot_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
}
@Test
- public void testUnsetOnBackPressedOnTaskRoot() {
+ public void testUnsetOnBackPressedOnTaskRoot_legacyTransitions() {
+ assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
mTaskView.onTaskAppeared(mTaskInfo, mLeash);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
mTaskView.onTaskVanished(mTaskInfo);
verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
}
+
+ @Test
+ public void testOnNewTask_noSurface() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+
+ verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+ verify(mViewListener, never()).onInitialized();
+ // If there's no surface the task should be made invisible
+ verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+ }
+
+ @Test
+ public void testSurfaceCreated_noTask() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ verify(mTaskViewTransitions, never()).setTaskViewVisible(any(), anyBoolean());
+
+ verify(mViewListener).onInitialized();
+ // No task, no visibility change
+ verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testOnNewTask_withSurface() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+
+ verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+ verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testSurfaceCreated_withTask() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+
+ verify(mViewListener).onInitialized();
+ verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskView), eq(true));
+
+ mTaskView.prepareOpenAnimation(false /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+
+ verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(true));
+ }
+
+ @Test
+ public void testSurfaceDestroyed_noTask() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ SurfaceHolder sh = mock(SurfaceHolder.class);
+ mTaskView.surfaceCreated(sh);
+ mTaskView.surfaceDestroyed(sh);
+
+ verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testSurfaceDestroyed_withTask() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ SurfaceHolder sh = mock(SurfaceHolder.class);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskView.surfaceCreated(sh);
+ reset(mViewListener);
+ mTaskView.surfaceDestroyed(sh);
+
+ verify(mTaskViewTransitions).setTaskViewVisible(eq(mTaskView), eq(false));
+
+ mTaskView.prepareHideAnimation(new SurfaceControl.Transaction());
+
+ verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+ }
+
+ @Test
+ public void testOnReleased() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ mTaskView.release();
+
+ verify(mOrganizer).removeListener(eq(mTaskView));
+ verify(mViewListener).onReleased();
+ verify(mTaskViewTransitions).removeTaskView(eq(mTaskView));
+ }
+
+ @Test
+ public void testOnTaskVanished() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ mTaskView.prepareCloseAnimation();
+
+ verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
+ }
+
+ @Test
+ public void testOnBackPressedOnTaskRoot() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
+
+ verify(mViewListener).onBackPressedOnTaskRoot(eq(mTaskInfo.taskId));
+ }
+
+ @Test
+ public void testSetOnBackPressedOnTaskRoot() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
+ }
+
+ @Test
+ public void testUnsetOnBackPressedOnTaskRoot() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTaskView.prepareOpenAnimation(true /* newTask */, new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(), mTaskInfo, mLeash, wct);
+ verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
+
+ mTaskView.prepareCloseAnimation();
+ verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index 2b5cd601b200..51eec27cfc0e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -18,6 +18,7 @@ package com.android.wm.shell;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -36,6 +37,7 @@ public final class TestRunningTaskInfoBuilder {
private WindowContainerToken mToken = createMockWCToken();
private int mParentTaskId = INVALID_TASK_ID;
private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
+ private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
public static WindowContainerToken createMockWCToken() {
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
@@ -60,6 +62,12 @@ public final class TestRunningTaskInfoBuilder {
return this;
}
+ public TestRunningTaskInfoBuilder setWindowingMode(
+ @WindowConfiguration.WindowingMode int windowingMode) {
+ mWindowingMode = windowingMode;
+ return this;
+ }
+
public ActivityManager.RunningTaskInfo build() {
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.parentTaskId = INVALID_TASK_ID;
@@ -67,6 +75,7 @@ public final class TestRunningTaskInfoBuilder {
info.parentTaskId = mParentTaskId;
info.configuration.windowConfiguration.setBounds(mBounds);
info.configuration.windowConfiguration.setActivityType(mActivityType);
+ info.configuration.windowConfiguration.setWindowingMode(mWindowingMode);
info.token = mToken;
info.isResizeable = true;
info.supportsMultiWindow = true;
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
new file mode 100644
index 000000000000..21ced0dc5981
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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 org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.IActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.ShellExecutor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * atest WMShellUnitTests:BackAnimationControllerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class BackAnimationControllerTest {
+
+ private final ShellExecutor mShellExecutor = new TestShellExecutor();
+
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private SurfaceControl.Transaction mTransaction;
+
+ @Mock
+ private IActivityTaskManager mActivityTaskManager;
+
+ private BackAnimationController mController;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mController = new BackAnimationController(
+ mShellExecutor, mTransaction, mActivityTaskManager, mContext);
+ }
+
+ private void createNavigationInfo(SurfaceControl topWindowLeash,
+ SurfaceControl screenshotSurface,
+ HardwareBuffer hardwareBuffer) {
+ BackNavigationInfo navigationInfo = new BackNavigationInfo(
+ BackNavigationInfo.TYPE_RETURN_TO_HOME,
+ topWindowLeash,
+ screenshotSurface,
+ hardwareBuffer,
+ new WindowConfiguration(),
+ new RemoteCallback((bundle) -> {}),
+ null);
+ try {
+ doReturn(navigationInfo).when(mActivityTaskManager).startBackNavigation();
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ }
+
+ @Test
+ public void screenshotAttachedAndVisible() {
+ SurfaceControl topWindowLeash = new SurfaceControl();
+ SurfaceControl screenshotSurface = new SurfaceControl();
+ HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
+ createNavigationInfo(topWindowLeash, screenshotSurface, hardwareBuffer);
+ mController.onMotionEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0));
+ verify(mTransaction).setBuffer(screenshotSurface, hardwareBuffer);
+ verify(mTransaction).setVisibility(screenshotSurface, true);
+ verify(mTransaction).apply();
+ }
+
+ @Test
+ public void surfaceMovesWithGesture() {
+ SurfaceControl topWindowLeash = new SurfaceControl();
+ SurfaceControl screenshotSurface = new SurfaceControl();
+ HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
+ createNavigationInfo(topWindowLeash, screenshotSurface, hardwareBuffer);
+ mController.onMotionEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0));
+ mController.onMotionEvent(MotionEvent.obtain(10, 0, MotionEvent.ACTION_MOVE, 100, 100, 0));
+ verify(mTransaction).setPosition(topWindowLeash, 100, 100);
+ verify(mTransaction, atLeastOnce()).apply();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 8bc1223cfd64..185479b145af 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -794,7 +794,7 @@ public class BubbleDataTest extends ShellTestCase {
}
@Test
- public void test_expanded_removeLastBubble_showsOverflowIfNotEmpty() {
+ public void test_expanded_removeLastBubble_collapsesIfOverflowNotEmpty() {
// Setup
sendUpdatedEntryAtTime(mEntryA1, 1000);
changeExpandedStateAtTime(true, 2000);
@@ -804,7 +804,7 @@ public class BubbleDataTest extends ShellTestCase {
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertThat(mBubbleData.getOverflowBubbles().size()).isGreaterThan(0);
- assertSelectionChangedTo(mBubbleData.getOverflow());
+ assertExpandedChangedTo(false);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 453050fcfab4..83d5f04b7cdb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -101,14 +102,21 @@ public class SplitLayoutTests extends ShellTestCase {
@Test
public void testSetDividePosition() {
- mSplitLayout.setDividePosition(anyInt());
+ mSplitLayout.setDividePosition(100, false /* applyLayoutChange */);
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(100);
+ verify(mSplitLayoutHandler, never()).onLayoutSizeChanged(any(SplitLayout.class));
+
+ mSplitLayout.setDividePosition(200, true /* applyLayoutChange */);
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(200);
verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
}
@Test
public void testSetDivideRatio() {
+ mSplitLayout.setDividePosition(200, false /* applyLayoutChange */);
mSplitLayout.setDivideRatio(0.5f);
- verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(
+ mSplitLayout.mDividerSnapAlgorithm.getMiddleTarget().position);
}
@Test
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 f622edb7f134..741da3fe9f58 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
@@ -16,6 +16,10 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+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 com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -29,6 +33,9 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
@@ -90,8 +97,8 @@ public class CompatUIControllerTest extends ShellTestCase {
mController = new CompatUIController(mContext, mMockDisplayController,
mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
@Override
- CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
- Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
+ CompatUIWindowManager createLayout(Context context, TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
return mMockLayout;
}
};
@@ -106,23 +113,41 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnCompatInfoChanged() {
- final Configuration taskConfig = new Configuration();
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
- // Verify that the restart button is added with non-null size compat info.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ // Verify that the compat controls are added with non-null size compat info.
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig),
- eq(mMockTaskListener));
+ verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
- // Verify that the restart button is updated with non-null new size compat info.
- final Configuration newTaskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
+ // Verify that the compat controls are updated with non-null new size compat info.
+ taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
- true /* show */);
+ verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, true /* canShow */);
- // Verify that the restart button is removed with null size compat info.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
+ // Verify that compat controls are removed with null compat info.
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
+ null /* taskListener */);
+
+ verify(mMockLayout).release();
+
+ clearInvocations(mMockLayout);
+ clearInvocations(mController);
+ // Verify that compat controls are removed with no size compat and dismissed camera state.
+ taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
+
+ verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
+
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_DISMISSED),
+ null /* taskListener */);
verify(mMockLayout).release();
}
@@ -139,8 +164,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnDisplayRemoved() {
mController.onDisplayAdded(DISPLAY_ID);
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
mMockTaskListener);
mController.onDisplayRemoved(DISPLAY_ID + 1);
@@ -157,16 +182,14 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnDisplayConfigurationChanged() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
- mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
- final Configuration newTaskConfig = new Configuration();
- mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, newTaskConfig);
+ mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, new Configuration());
verify(mMockLayout, never()).updateDisplayLayout(any());
- mController.onDisplayConfigurationChanged(DISPLAY_ID, newTaskConfig);
+ mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
}
@@ -174,9 +197,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testInsetsChanged() {
mController.onDisplayAdded(DISPLAY_ID);
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
- mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
InsetsState insetsState = new InsetsState();
InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
insetsSource.setFrame(0, 0, 1000, 1000);
@@ -196,8 +218,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testChangeButtonVisibilityOnImeShowHide() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after IME is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
@@ -205,10 +227,11 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while IME is showing.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
- false /* show */);
+ verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, false /* canShow */);
// Verify button is shown after IME is hidden.
mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
@@ -218,8 +241,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after keyguard becomes occluded.
mController.onKeyguardOccludedChanged(true);
@@ -227,10 +250,11 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while keyguard is occluded.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
- false /* show */);
+ verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, false /* canShow */);
// Verify button is shown after keyguard becomes not occluded.
mController.onKeyguardOccludedChanged(false);
@@ -240,8 +264,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onKeyguardOccludedChanged(true);
@@ -263,8 +287,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onKeyguardOccludedChanged(true);
@@ -283,4 +307,14 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(true);
}
+
+ private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.displayId = displayId;
+ taskInfo.topActivityInSizeCompat = hasSizeCompat;
+ taskInfo.cameraCompatControlState = cameraCompatControlState;
+ return taskInfo;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 2c3987bc358d..211781798af7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -16,12 +16,20 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+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 com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
@@ -69,7 +77,7 @@ public class CompatUILayoutTest extends ShellTestCase {
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
- false /* hasShownHint */);
+ false /* hasShownSizeCompatHint */, false /* hasShownCameraCompatHint */);
mCompatUILayout = (CompatUILayout)
LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
@@ -78,6 +86,7 @@ public class CompatUILayoutTest extends ShellTestCase {
spyOn(mWindowManager);
spyOn(mCompatUILayout);
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+ doReturn(mCompatUILayout).when(mWindowManager).inflateLayout();
}
@Test
@@ -86,7 +95,6 @@ public class CompatUILayoutTest extends ShellTestCase {
button.performClick();
verify(mWindowManager).onRestartButtonClicked();
- doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
}
@@ -102,10 +110,100 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testOnClickForSizeCompatHint() {
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
sizeCompatHint.performClick();
verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false);
}
+
+ @Test
+ public void testUpdateCameraTreatmentButton_treatmentAppliedByDefault() {
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED));
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraTreatmentButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ button.performClick();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ }
+
+ @Test
+ public void testUpdateCameraTreatmentButton_treatmentSuggestedByDefault() {
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraTreatmentButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ button.performClick();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
+ public void testOnCameraDismissButtonClicked() {
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraDismissButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
+ }
+
+ @Test
+ public void testOnLongClickForCameraTreatementButton() {
+ doNothing().when(mWindowManager).onCameraButtonLongClicked();
+
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performLongClick();
+
+ verify(mWindowManager).onCameraButtonLongClicked();
+ }
+
+ @Test
+ public void testOnLongClickForCameraDismissButton() {
+ doNothing().when(mWindowManager).onCameraButtonLongClicked();
+
+ final ImageButton button = mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
+ button.performLongClick();
+
+ verify(mWindowManager).onCameraButtonLongClicked();
+ }
+
+ @Test
+ public void testOnClickForCameraCompatHint() {
+ mWindowManager.createLayout(true /* show */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
+ final LinearLayout hint = mCompatUILayout.findViewById(R.id.camera_compat_hint);
+ hint.performClick();
+
+ verify(mCompatUILayout).setCameraCompatHintVisibility(/* show= */ false);
+ }
+
+ private static TaskInfo createTaskInfo(boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.topActivityInSizeCompat = hasSizeCompat;
+ taskInfo.cameraCompatControlState = cameraCompatControlState;
+ return taskInfo;
+ }
}
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 d5dcf2e11a46..de882eae1503 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
@@ -16,12 +16,17 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+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 com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
@@ -30,6 +35,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -70,54 +77,56 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private CompatUILayout mCompatUILayout;
@Mock private SurfaceControlViewHost mViewHost;
- private Configuration mTaskConfig;
private CompatUIWindowManager mWindowManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTaskConfig = new Configuration();
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
- false /* hasShownHint */);
+ false /* hasShownSizeCompatHint */, false /* hasShownSizeCompatHint */);
spyOn(mWindowManager);
- doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+ doReturn(mCompatUILayout).when(mWindowManager).inflateLayout();
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
}
@Test
public void testCreateSizeCompatButton() {
// Not create layout if show is false.
- mWindowManager.createLayout(false /* show */);
+ mWindowManager.createLayout(false /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager, never()).inflateCompatUILayout();
+ verify(mWindowManager, never()).inflateLayout();
// Not create hint popup.
- mWindowManager.mShouldShowHint = false;
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.mShouldShowSizeCompatHint = false;
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager).inflateCompatUILayout();
- verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+ verify(mWindowManager).inflateLayout();
+ verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
// Create hint popup.
mWindowManager.release();
- mWindowManager.mShouldShowHint = true;
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.mShouldShowSizeCompatHint = true;
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager, times(2)).inflateCompatUILayout();
+ verify(mWindowManager, times(2)).inflateLayout();
assertNotNull(mCompatUILayout);
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
- assertFalse(mWindowManager.mShouldShowHint);
+ assertFalse(mWindowManager.mShouldShowSizeCompatHint);
}
@Test
public void testRelease() {
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
mWindowManager.release();
@@ -126,11 +135,12 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testUpdateCompatInfo() {
- mWindowManager.createLayout(true /* show */);
+ TaskInfo taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, taskInfo);
// No diff
clearInvocations(mWindowManager);
- mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
verify(mWindowManager, never()).updateSurfacePosition();
verify(mWindowManager, never()).release();
@@ -140,23 +150,89 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
clearInvocations(mWindowManager);
final ShellTaskOrganizer.TaskListener newTaskListener = mock(
ShellTaskOrganizer.TaskListener.class);
- mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
- true /* show */);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mWindowManager).release();
- verify(mWindowManager).createLayout(anyBoolean());
+ verify(mWindowManager).createLayout(true);
+
+ // Change in Size Compat to false, hides restart button.
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
+
+ verify(mCompatUILayout).setRestartButtonVisibility(/* show */ false);
+
+ // Change in Size Compat to true, shows restart button.
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
+
+ verify(mCompatUILayout).setRestartButtonVisibility(/* show */ true);
+
+ // Change Camera Compat state, show a control.
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ // Change Camera Compat state, update a control.
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Change Camera Compat state to hidden, hide a control.
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
// Change task bounds, update position.
clearInvocations(mWindowManager);
- final Configuration newTaskConfiguration = new Configuration();
- newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
- mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener,
- true /* show */);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mWindowManager).updateSurfacePosition();
}
@Test
+ public void testUpdateCompatInfoLayoutNotInflatedYet() {
+ TaskInfo taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(false /* canShow */, taskInfo);
+
+ verify(mWindowManager, never()).inflateLayout();
+
+ // Change topActivityInSizeCompat to false and pass canShow true, layout shouldn't be
+ // inflated
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
+
+ verify(mWindowManager, never()).inflateLayout();
+
+ // Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated.
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
+
+ verify(mWindowManager).inflateLayout();
+ }
+
+ @Test
public void testUpdateDisplayLayout() {
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 1000;
@@ -200,24 +276,25 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testUpdateVisibility() {
// Create button if it is not created.
- mWindowManager.mCompatUILayout = null;
- mWindowManager.updateVisibility(true /* show */);
+ mWindowManager.mLayout = null;
+ mWindowManager.mHasSizeCompat = true;
+ mWindowManager.updateVisibility(true /* canShow */);
- verify(mWindowManager).createLayout(true /* show */);
+ verify(mWindowManager).createLayout(true /* canShow */);
// Hide button.
clearInvocations(mWindowManager);
doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
- mWindowManager.updateVisibility(false /* show */);
+ mWindowManager.updateVisibility(false /* canShow */);
- verify(mWindowManager, never()).createLayout(anyBoolean());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), any());
verify(mCompatUILayout).setVisibility(View.GONE);
// Show button.
doReturn(View.GONE).when(mCompatUILayout).getVisibility();
- mWindowManager.updateVisibility(true /* show */);
+ mWindowManager.updateVisibility(true /* canShow */);
- verify(mWindowManager, never()).createLayout(anyBoolean());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), any());
verify(mCompatUILayout).setVisibility(View.VISIBLE);
}
@@ -230,6 +307,37 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
}
@Test
+ public void testOnCameraDismissButtonClicked() {
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
+ clearInvocations(mCompatUILayout);
+ mWindowManager.onCameraDismissButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
+ verify(mCompatUILayout).setCameraControlVisibility(/* show= */ false);
+ }
+
+ @Test
+ public void testOnCameraTreatmentButtonClicked() {
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
+ clearInvocations(mCompatUILayout);
+ mWindowManager.onCameraTreatmentButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ mWindowManager.onCameraTreatmentButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
public void testOnRestartButtonClicked() {
mWindowManager.onRestartButtonClicked();
@@ -239,15 +347,67 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testOnRestartButtonLongClicked_showHint() {
// Not create hint popup.
- mWindowManager.mShouldShowHint = false;
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.mShouldShowSizeCompatHint = false;
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager).inflateCompatUILayout();
- verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+ verify(mWindowManager).inflateLayout();
+ verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
mWindowManager.onRestartButtonLongClicked();
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
}
+ @Test
+ public void testOnCamerControlLongClicked_showHint() {
+ // Not create hint popup.
+ mWindowManager.mShouldShowCameraCompatHint = false;
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
+
+ verify(mWindowManager).inflateLayout();
+ verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
+
+ mWindowManager.onCameraButtonLongClicked();
+
+ verify(mCompatUILayout).setCameraCompatHintVisibility(true /* show */);
+ }
+
+ @Test
+ public void testCreateCameraCompatControl() {
+ // Not create layout if show is false.
+ mWindowManager.createLayout(false /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
+
+ verify(mWindowManager, never()).inflateLayout();
+
+ // Not create hint popup.
+ mWindowManager.mShouldShowCameraCompatHint = false;
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
+
+ verify(mWindowManager).inflateLayout();
+ verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
+ verify(mCompatUILayout).setCameraControlVisibility(true /* show */);
+
+ // Create hint popup.
+ mWindowManager.release();
+ mWindowManager.mShouldShowCameraCompatHint = true;
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
+
+ verify(mWindowManager, times(2)).inflateLayout();
+ assertNotNull(mCompatUILayout);
+ verify(mCompatUILayout, times(2)).setCameraControlVisibility(true /* show */);
+ assertFalse(mWindowManager.mShouldShowCameraCompatHint);
+ }
+
+ private static TaskInfo createTaskInfo(boolean hasSizeCompat,
+ @TaskInfo.CameraCompatControlState int cameraCompatControlState) {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.topActivityInSizeCompat = hasSizeCompat;
+ taskInfo.cameraCompatControlState = cameraCompatControlState;
+ return taskInfo;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index fe66e225ad4a..35e498262707 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -19,7 +19,6 @@ package com.android.wm.shell.draganddrop;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
@@ -111,7 +110,6 @@ public class DragAndDropPolicyTest {
private ActivityManager.RunningTaskInfo mHomeTask;
private ActivityManager.RunningTaskInfo mFullscreenAppTask;
private ActivityManager.RunningTaskInfo mNonResizeableFullscreenAppTask;
- private ActivityManager.RunningTaskInfo mSplitPrimaryAppTask;
@Before
public void setUp() throws RemoteException {
@@ -144,8 +142,6 @@ public class DragAndDropPolicyTest {
mNonResizeableFullscreenAppTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
mNonResizeableFullscreenAppTask.isResizeable = false;
- mSplitPrimaryAppTask = createTaskInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
- ACTIVITY_TYPE_STANDARD);
setRunningTask(mFullscreenAppTask);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
index 9cbdf1e2dbb6..46fe201072c9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.fullscreen;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
@@ -30,6 +31,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.os.SystemProperties;
import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
@@ -47,6 +49,8 @@ import java.util.Optional;
@SmallTest
public class FullscreenTaskListenerTest {
+ private static final boolean ENABLE_SHELL_TRANSITIONS =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false);
@Mock
private SyncTransactionQueue mSyncQueue;
@@ -71,6 +75,7 @@ public class FullscreenTaskListenerTest {
@Test
public void testAnimatableTaskAppeared_notifiesUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo info = createTaskInfo(/* visible */ true, /* taskId */ 0);
mListener.onTaskAppeared(info, mSurfaceControl);
@@ -80,6 +85,7 @@ public class FullscreenTaskListenerTest {
@Test
public void testMultipleAnimatableTasksAppeared_notifiesUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo animatable1 = createTaskInfo(/* visible */ true, /* taskId */ 0);
RunningTaskInfo animatable2 = createTaskInfo(/* visible */ true, /* taskId */ 1);
@@ -93,6 +99,7 @@ public class FullscreenTaskListenerTest {
@Test
public void testNonAnimatableTaskAppeared_doesNotNotifyUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
mListener.onTaskAppeared(info, mSurfaceControl);
@@ -102,6 +109,7 @@ public class FullscreenTaskListenerTest {
@Test
public void testNonAnimatableTaskChanged_doesNotNotifyUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
mListener.onTaskAppeared(info, mSurfaceControl);
@@ -112,6 +120,7 @@ public class FullscreenTaskListenerTest {
@Test
public void testNonAnimatableTaskVanished_doesNotNotifyUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
mListener.onTaskAppeared(info, mSurfaceControl);
@@ -122,6 +131,7 @@ public class FullscreenTaskListenerTest {
@Test
public void testAnimatableTaskBecameInactive_notifiesUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo animatableTask = createTaskInfo(/* visible */ true, /* taskId */ 0);
mListener.onTaskAppeared(animatableTask, mSurfaceControl);
RunningTaskInfo notAnimatableTask = createTaskInfo(/* visible */ false, /* taskId */ 0);
@@ -133,6 +143,7 @@ public class FullscreenTaskListenerTest {
@Test
public void testAnimatableTaskVanished_notifiesUnfoldController() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
RunningTaskInfo taskInfo = createTaskInfo(/* visible */ true, /* taskId */ 0);
mListener.onTaskAppeared(taskInfo, mSurfaceControl);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java
new file mode 100644
index 000000000000..f3f70673b332
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java
@@ -0,0 +1,61 @@
+/**
+ * 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.onehanded;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.TestableLooper;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link BackgroundWindowManager} */
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4.class)
+public class BackgroundWindowManagerTest extends ShellTestCase {
+ private BackgroundWindowManager mBackgroundWindowManager;
+ @Mock
+ private DisplayLayout mMockDisplayLayout;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mBackgroundWindowManager = new BackgroundWindowManager(mContext);
+ mBackgroundWindowManager.onDisplayChanged(mMockDisplayLayout);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testInitRelease() {
+ mBackgroundWindowManager.initView();
+ assertThat(mBackgroundWindowManager.getSurfaceControl()).isNotNull();
+
+ mBackgroundWindowManager.removeBackgroundLayer();
+ assertThat(mBackgroundWindowManager.getSurfaceControl()).isNull();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
deleted file mode 100644
index 7b9553c5ef9b..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
+++ /dev/null
@@ -1,131 +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.onehanded;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
-
-import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
-import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.Display;
-import android.view.SurfaceControl;
-import android.window.DisplayAreaInfo;
-import android.window.IWindowContainerToken;
-import android.window.WindowContainerToken;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase {
- private DisplayAreaInfo mDisplayAreaInfo;
- private Display mDisplay;
- private DisplayLayout mDisplayLayout;
- private OneHandedBackgroundPanelOrganizer mSpiedBackgroundPanelOrganizer;
- private WindowContainerToken mToken;
- private SurfaceControl mLeash;
-
- @Mock
- IWindowContainerToken mMockRealToken;
- @Mock
- DisplayController mMockDisplayController;
- @Mock
- OneHandedSettingsUtil mMockSettingsUtil;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mToken = new WindowContainerToken(mMockRealToken);
- mLeash = new SurfaceControl();
- mDisplay = mContext.getDisplay();
- mDisplayLayout = new DisplayLayout(mContext, mDisplay);
- when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
- mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
- FEATURE_ONE_HANDED_BACKGROUND_PANEL);
-
- mSpiedBackgroundPanelOrganizer = spy(
- new OneHandedBackgroundPanelOrganizer(mContext, mDisplayLayout, mMockSettingsUtil,
- Runnable::run));
- mSpiedBackgroundPanelOrganizer.onDisplayChanged(mDisplayLayout);
- }
-
- @Test
- public void testOnDisplayAreaAppeared() {
- mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-
- assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isTrue();
- verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
- }
-
- @Test
- public void testShowBackgroundLayer() {
- mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, null);
- mSpiedBackgroundPanelOrganizer.onStart();
-
- verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
- }
-
- @Test
- public void testRemoveBackgroundLayer() {
- mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-
- assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isNotNull();
-
- reset(mSpiedBackgroundPanelOrganizer);
- mSpiedBackgroundPanelOrganizer.removeBackgroundPanelLayer();
-
- assertThat(mSpiedBackgroundPanelOrganizer.mBackgroundSurface).isNull();
- }
-
- @Test
- public void testStateNone_onConfigurationChanged() {
- mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_NONE);
- mSpiedBackgroundPanelOrganizer.onConfigurationChanged();
-
- verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
- }
-
- @Test
- public void testStateActivate_onConfigurationChanged() {
- mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_ACTIVE);
- mSpiedBackgroundPanelOrganizer.onConfigurationChanged();
-
- verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 0a3a84923053..2886b97a3020 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -46,6 +46,7 @@ import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -72,8 +73,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
DisplayController mMockDisplayController;
@Mock
- OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
- @Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
OneHandedEventCallback mMockEventCallback;
@@ -86,6 +85,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
+ InteractionJankMonitor mMockJankMonitor;
+ @Mock
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
@@ -109,9 +110,9 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mSpiedTransitionState = spy(new OneHandedState());
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
+ when(mMockDisplayController.getDisplayLayout(anyInt())).thenReturn(null);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
- when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
mDefaultEnabled);
when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
@@ -130,7 +131,6 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
mMockDisplayController,
- mMockBackgroundOrganizer,
mMockDisplayAreaOrganizer,
mMockTouchHandler,
mMockTutorialHandler,
@@ -138,6 +138,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
mSpiedTransitionState,
+ mMockJankMonitor,
mMockUiEventLogger,
mMockOverlayManager,
mMockTaskStackListener,
@@ -153,6 +154,13 @@ public class OneHandedControllerTest extends OneHandedTestCase {
}
@Test
+ public void testNullDisplayLayout() {
+ mSpiedOneHandedController.updateDisplayLayout(0);
+
+ verify(mMockDisplayAreaOrganizer, never()).setDisplayLayout(any());
+ }
+
+ @Test
public void testStartOneHandedShouldTriggerScheduleOffset() {
mSpiedTransitionState.setState(STATE_NONE);
mSpiedOneHandedController.setOneHandedEnabled(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index ef16fd391235..9c7f7237871a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -50,6 +50,7 @@ import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -94,11 +95,11 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
@Mock
WindowContainerTransaction mMockWindowContainerTransaction;
@Mock
- OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
- @Mock
ShellExecutor mMockShellMainExecutor;
@Mock
OneHandedSettingsUtil mMockSettingsUitl;
+ @Mock
+ InteractionJankMonitor mJankMonitor;
List<DisplayAreaAppearedInfo> mDisplayAreaAppearedInfoList = new ArrayList<>();
@@ -140,7 +141,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mMockSettingsUitl,
mMockAnimationController,
mTutorialHandler,
- mMockBackgroundOrganizer,
+ mJankMonitor,
mMockShellMainExecutor));
for (int i = 0; i < DISPLAYAREA_INFO_COUNT; i++) {
@@ -427,9 +428,16 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
mMockSettingsUitl,
mMockAnimationController,
mTutorialHandler,
- mMockBackgroundOrganizer,
+ mJankMonitor,
mMockShellMainExecutor));
assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse();
}
+
+ @Test
+ public void testDisplayArea_setDisplayLayout_should_updateDisplayBounds() {
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+
+ verify(mSpiedDisplayAreaOrganizer).updateDisplayBounds();
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index bea69c5d80ef..dba1b8b86261 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -40,6 +40,7 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -66,8 +67,6 @@ public class OneHandedStateTest extends OneHandedTestCase {
@Mock
DisplayController mMockDisplayController;
@Mock
- OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
- @Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
OneHandedTouchHandler mMockTouchHandler;
@@ -78,6 +77,8 @@ public class OneHandedStateTest extends OneHandedTestCase {
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
+ InteractionJankMonitor mMockJankMonitor;
+ @Mock
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
@@ -102,7 +103,6 @@ public class OneHandedStateTest extends OneHandedTestCase {
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
- when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
mDefaultEnabled);
when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
@@ -120,7 +120,6 @@ public class OneHandedStateTest extends OneHandedTestCase {
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
mMockDisplayController,
- mMockBackgroundOrganizer,
mMockDisplayAreaOrganizer,
mMockTouchHandler,
mMockTutorialHandler,
@@ -128,6 +127,7 @@ public class OneHandedStateTest extends OneHandedTestCase {
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
mSpiedState,
+ mMockJankMonitor,
mMockUiEventLogger,
mMockOverlayManager,
mMockTaskStackListener,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index b1434ca325b7..63d8bfd1e7ef 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -56,6 +56,8 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
OneHandedSettingsUtil mMockSettingsUtil;
@Mock
WindowManager mMockWindowManager;
+ @Mock
+ BackgroundWindowManager mMockBackgroundWindowManager;
@Before
public void setUp() {
@@ -63,10 +65,11 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
when(mMockSettingsUtil.getTutorialShownCounts(any(), anyInt())).thenReturn(0);
mDisplay = mContext.getDisplay();
- mDisplayLayout = new DisplayLayout(mContext, mDisplay);
+ mDisplayLayout = new DisplayLayout(getTestContext().getApplicationContext(), mDisplay);
mSpiedTransitionState = spy(new OneHandedState());
mSpiedTutorialHandler = spy(
- new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager));
+ new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager,
+ mMockBackgroundWindowManager));
mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index aab1e3a99c98..dda1a8295e51 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -31,6 +31,7 @@ import android.window.WindowContainerToken;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
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.SyncTransactionQueue;
@@ -69,15 +70,15 @@ public class SplitTestUtils {
TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
- MainStage mainStage, SideStage sideStage, DisplayImeController imeController,
- DisplayInsetsController insetsController, SplitLayout splitLayout,
- Transitions transitions, TransactionPool transactionPool,
+ MainStage mainStage, SideStage sideStage, DisplayController displayController,
+ DisplayImeController imeController, DisplayInsetsController insetsController,
+ SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger,
Optional<RecentTasksController> recentTasks,
Provider<Optional<StageTaskUnfoldController>> unfoldController) {
super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage,
- sideStage, imeController, insetsController, splitLayout, transitions,
- transactionPool, logger, recentTasks, unfoldController);
+ sideStage, displayController, imeController, insetsController, splitLayout,
+ transitions, transactionPool, logger, recentTasks, unfoldController);
// Prepare default TaskDisplayArea for testing.
mDisplayAreaInfo = new DisplayAreaInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 1eae625233a0..59c377a3e13d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.splitscreen;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -24,7 +25,11 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitTestUtils.createMockSurface;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -60,13 +65,13 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.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.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -85,6 +90,7 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
+ @Mock private DisplayController mDisplayController;
@Mock private DisplayImeController mDisplayImeController;
@Mock private DisplayInsetsController mDisplayInsetsController;
@Mock private TransactionPool mTransactionPool;
@@ -120,8 +126,8 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
- mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool, mLogger, Optional.empty(), Optional::empty);
+ mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout,
+ mTransitions, mTransactionPool, mLogger, Optional.empty(), Optional::empty);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
@@ -133,6 +139,40 @@ public class SplitTransitionTests extends ShellTestCase {
}
@Test
+ public void testLaunchToSide() {
+ ActivityManager.RunningTaskInfo newTask = new TestRunningTaskInfoBuilder()
+ .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
+ ActivityManager.RunningTaskInfo reparentTask = new TestRunningTaskInfoBuilder()
+ .setParentTaskId(mMainStage.mRootTaskInfo.taskId).build();
+
+ // Create a request to start a new task in side stage
+ TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, newTask, null);
+ IBinder transition = mock(IBinder.class);
+ WindowContainerTransaction result =
+ mStageCoordinator.handleRequest(transition, request);
+
+ // it should handle the transition to enter split screen.
+ assertNotNull(result);
+ assertTrue(containsSplitEnter(result));
+
+ // simulate the transition
+ TransitionInfo.Change openChange = createChange(TRANSIT_OPEN, newTask);
+ TransitionInfo.Change reparentChange = createChange(TRANSIT_CHANGE, reparentTask);
+
+ TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
+ info.addChange(openChange);
+ info.addChange(reparentChange);
+ mSideStage.onTaskAppeared(newTask, createMockSurface());
+ mMainStage.onTaskAppeared(reparentTask, createMockSurface());
+ boolean accepted = mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(accepted);
+ assertTrue(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
public void testLaunchPair() {
TransitionInfo info = createEnterPairInfo();
@@ -211,18 +251,20 @@ public class SplitTransitionTests extends ShellTestCase {
}
@Test
- public void testDismissToHome() {
+ public void testEnterRecents() {
enterSplit();
ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
- .setActivityType(ACTIVITY_TYPE_HOME).build();
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setActivityType(ACTIVITY_TYPE_HOME)
+ .build();
// Create a request to bring home forward
TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null);
IBinder transition = mock(IBinder.class);
WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
- assertTrue(containsSplitExit(result));
+ assertTrue(result.isEmpty());
// make sure we haven't made any local changes yet (need to wait until transition is ready)
assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -242,6 +284,64 @@ public class SplitTransitionTests extends ShellTestCase {
mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
+ public void testDismissFromBeingOccluded() {
+ enterSplit();
+
+ ActivityManager.RunningTaskInfo normalTask = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build();
+
+ // Create a request to bring a normal task forward
+ TransitionRequestInfo request =
+ new TransitionRequestInfo(TRANSIT_TO_FRONT, normalTask, null);
+ IBinder transition = mock(IBinder.class);
+ WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
+
+ assertTrue(containsSplitExit(result));
+
+ // make sure we haven't made any local changes yet (need to wait until transition is ready)
+ assertTrue(mStageCoordinator.isSplitScreenVisible());
+
+ // simulate the transition
+ TransitionInfo.Change normalChange = createChange(TRANSIT_TO_FRONT, normalTask);
+ TransitionInfo.Change mainChange = createChange(TRANSIT_TO_BACK, mMainChild);
+ TransitionInfo.Change sideChange = createChange(TRANSIT_TO_BACK, mSideChild);
+
+ TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
+ info.addChange(normalChange);
+ info.addChange(mainChange);
+ info.addChange(sideChange);
+ mMainStage.onTaskVanished(mMainChild);
+ mSideStage.onTaskVanished(mSideChild);
+ mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertFalse(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
+ public void testDismissFromMultiWindowSupport() {
+ enterSplit();
+
+ // simulate the transition
+ TransitionInfo.Change mainChange = createChange(TRANSIT_TO_BACK, mMainChild);
+ TransitionInfo.Change sideChange = createChange(TRANSIT_TO_BACK, mSideChild);
+ TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
+ info.addChange(mainChange);
+ info.addChange(sideChange);
+ IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
+ new WindowContainerTransaction(), mStageCoordinator,
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE);
+ boolean accepted = mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(accepted);
assertFalse(mStageCoordinator.isSplitScreenVisible());
}
@@ -256,8 +356,9 @@ public class SplitTransitionTests extends ShellTestCase {
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
info.addChange(mainChange);
info.addChange(sideChange);
- IBinder transition = mStageCoordinator.onSnappedToDismissTransition(
- false /* mainStageToTop */);
+ IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
+ new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER,
+ STAGE_TYPE_SIDE);
mMainStage.onTaskVanished(mMainChild);
mSideStage.onTaskVanished(mSideChild);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -324,6 +425,22 @@ public class SplitTransitionTests extends ShellTestCase {
true /* includingTopTask */);
}
+ private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) {
+ boolean movedMainToFront = false;
+ boolean movedSideToFront = false;
+ for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
+ WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
+ if (op.getType() == HIERARCHY_OP_TYPE_REORDER) {
+ if (op.getContainer() == mMainStage.mRootTaskInfo.token.asBinder()) {
+ movedMainToFront = true;
+ } else if (op.getContainer() == mSideStage.mRootTaskInfo.token.asBinder()) {
+ movedSideToFront = true;
+ }
+ }
+ }
+ return movedMainToFront && movedSideToFront;
+ }
+
private boolean containsSplitExit(@NonNull WindowContainerTransaction wct) {
// reparenting of child tasks to null constitutes exiting split.
boolean reparentedMain = false;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 85f6789c3435..099987a2f821 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -51,6 +51,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -91,6 +92,8 @@ public class StageCoordinatorTests extends ShellTestCase {
@Mock
private SplitLayout mSplitLayout;
@Mock
+ private DisplayController mDisplayController;
+ @Mock
private DisplayImeController mDisplayImeController;
@Mock
private DisplayInsetsController mDisplayInsetsController;
@@ -114,6 +117,7 @@ public class StageCoordinatorTests extends ShellTestCase {
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
+ when(mSplitLayout.isLandscape()).thenReturn(false);
}
@Test
@@ -168,8 +172,9 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator.onLayoutSizeChanged(mSplitLayout);
- verify(mMainUnfoldController).onLayoutChanged(mBounds2);
- verify(mSideUnfoldController).onLayoutChanged(mBounds1);
+ verify(mMainUnfoldController).onLayoutChanged(mBounds2, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ false);
+ verify(mSideUnfoldController).onLayoutChanged(mBounds1, SPLIT_POSITION_TOP_OR_LEFT, false);
}
@Test
@@ -180,8 +185,10 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator.onLayoutSizeChanged(mSplitLayout);
- verify(mMainUnfoldController).onLayoutChanged(mBounds1);
- verify(mSideUnfoldController).onLayoutChanged(mBounds2);
+ verify(mMainUnfoldController).onLayoutChanged(mBounds1, SPLIT_POSITION_TOP_OR_LEFT,
+ false);
+ verify(mSideUnfoldController).onLayoutChanged(mBounds2, SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ false);
}
@Test
@@ -289,7 +296,7 @@ public class StageCoordinatorTests extends ShellTestCase {
private StageCoordinator createStageCoordinator(SplitLayout splitLayout) {
return new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
- mDisplayImeController, mDisplayInsetsController, splitLayout,
+ mDisplayController, mDisplayImeController, mDisplayInsetsController, splitLayout,
mTransitions, mTransactionPool, mLogger, Optional.empty(),
new UnfoldControllerProvider());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 53d5076f5835..13b726efb046 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -132,6 +132,7 @@ public final class StageTaskListenerTests extends ShellTestCase {
@Test
public void testTaskAppeared_notifiesUnfoldListener() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final ActivityManager.RunningTaskInfo task =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
@@ -142,6 +143,7 @@ public final class StageTaskListenerTests extends ShellTestCase {
@Test
public void testTaskVanished_notifiesUnfoldListener() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final ActivityManager.RunningTaskInfo task =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
mStageTaskListener.onTaskAppeared(task, mSurfaceControl);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index e39171343bb9..0f4a06f22986 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -54,7 +54,9 @@ import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
@@ -84,8 +86,6 @@ import com.android.wm.shell.common.TransactionPool;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
@@ -106,6 +106,7 @@ public class ShellTransitionTests {
private final TestShellExecutor mMainExecutor = new TestShellExecutor();
private final ShellExecutor mAnimExecutor = new TestShellExecutor();
private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler();
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
@Before
public void setUp() {
@@ -752,7 +753,7 @@ public class ShellTransitionTests {
private Transitions createTestTransitions() {
return new Transitions(mOrganizer, mTransactionPool, createTestDisplayController(),
- mContext, mMainExecutor, mAnimExecutor);
+ mContext, mMainExecutor, mMainHandler, mAnimExecutor);
}
//
// private class TestDisplayController extends DisplayController {
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 0cde3d1242c8..136fc6ca4e2a 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -25,6 +25,7 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"
#include "androidfw/Util.h"
#include "utils/ByteOrder.h"
@@ -600,6 +601,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
return base::unexpected(result.error());
}
+ bool overlaid = false;
if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) {
for (const auto& id_map : package_group.overlays_) {
auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
@@ -616,6 +618,27 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(
Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, String8(), result->cookie});
+ if (auto path = apk_assets_[result->cookie]->GetPath()) {
+ const std::string overlay_path = path->data();
+ if (IsFabricatedOverlay(overlay_path)) {
+ // FRRO don't have package name so we use the creating package here.
+ String8 frro_name = String8("FRRO");
+ // Get the first part of it since the expected one should be like
+ // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
+ // under /data/resource-cache/.
+ const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
+ const size_t end = name.find('-');
+ if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
+ frro_name.append(base::StringPrintf(" created by %s",
+ name.substr(0 /* pos */,
+ end).c_str()).c_str());
+ }
+ last_resolution_.best_package_name = frro_name;
+ } else {
+ last_resolution_.best_package_name = result->package_name->c_str();
+ }
+ }
+ overlaid = true;
}
continue;
}
@@ -646,6 +669,9 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
last_resolution_.steps.push_back(
Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(),
overlay_result->cookie});
+ last_resolution_.best_package_name =
+ overlay_result->package_name->c_str();
+ overlaid = true;
}
}
}
@@ -654,6 +680,10 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
last_resolution_.cookie = result->cookie;
last_resolution_.type_string_ref = result->type_string_ref;
last_resolution_.entry_string_ref = result->entry_string_ref;
+ last_resolution_.best_config_name = result->config.toString();
+ if (!overlaid) {
+ last_resolution_.best_package_name = result->package_name->c_str();
+ }
}
return result;
@@ -671,8 +701,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
uint32_t best_offset = 0U;
uint32_t type_flags = 0U;
- std::vector<Resolution::Step> resolution_steps;
-
// If `desired_config` is not the same as the set configuration or the caller will accept a value
// from any configuration, then we cannot use our filtered list of types since it only it contains
// types matched to the set configuration.
@@ -725,7 +753,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
resolution_type = Resolution::Step::Type::OVERLAID;
} else {
if (UNLIKELY(logging_enabled)) {
- resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED,
+ last_resolution_.steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED,
this_config.toString(),
cookie});
}
@@ -742,7 +770,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
if (!offset.has_value()) {
if (UNLIKELY(logging_enabled)) {
- resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
+ last_resolution_.steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY,
this_config.toString(),
cookie});
}
@@ -806,6 +834,8 @@ void AssetManager2::ResetResourceResolution() const {
last_resolution_.steps.clear();
last_resolution_.type_string_ref = StringPoolRef();
last_resolution_.entry_string_ref = StringPoolRef();
+ last_resolution_.best_config_name.clear();
+ last_resolution_.best_package_name.clear();
}
void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
@@ -865,9 +895,34 @@ std::string AssetManager2::GetLastResourceResolution() const {
}
}
+ log_stream << "\nBest matching is from "
+ << (last_resolution_.best_config_name.isEmpty() ? "default"
+ : last_resolution_.best_config_name)
+ << " configuration of " << last_resolution_.best_package_name;
return log_stream.str();
}
+base::expected<uint32_t, NullOrIOError> AssetManager2::GetParentThemeResourceId(uint32_t resid)
+const {
+ auto entry = FindEntry(resid, 0u /* density_override */,
+ false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (!entry.has_value()) {
+ return base::unexpected(entry.error());
+ }
+
+ auto entry_map = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&entry->entry);
+ if (entry_map == nullptr) {
+ // Not a bag, nothing to do.
+ return base::unexpected(std::nullopt);
+ }
+
+ auto map = *entry_map;
+ const uint32_t parent_resid = dtohl(map->parent.ident);
+
+ return parent_resid;
+}
+
base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetResourceName(
uint32_t resid) const {
auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */,
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 13aff3812767..35b6170fae5b 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -650,22 +650,25 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
// Retrieve all the resource ids belonging to this policy chunk
- std::unordered_set<uint32_t> ids;
const auto ids_begin = overlayable_child_chunk.data_ptr().convert<ResTable_ref>();
const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
+ std::unordered_set<uint32_t> ids;
+ ids.reserve(ids_end - ids_begin);
for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
if (!id_iter) {
+ LOG(ERROR) << "NULL ResTable_ref record??";
return {};
}
ids.insert(dtohl(id_iter->ident));
}
// Add the pairing of overlayable properties and resource ids to the package
- OverlayableInfo overlayable_info{};
- overlayable_info.name = name;
- overlayable_info.actor = actor;
- overlayable_info.policy_flags = policy_header->policy_flags;
- loaded_package->overlayable_infos_.emplace_back(overlayable_info, ids);
+ OverlayableInfo overlayable_info {
+ .name = name,
+ .actor = actor,
+ .policy_flags = policy_header->policy_flags
+ };
+ loaded_package->overlayable_infos_.emplace_back(std::move(overlayable_info), std::move(ids));
loaded_package->defines_overlayable_ = true;
break;
}
@@ -692,7 +695,6 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
break;
}
- std::unordered_set<uint32_t> finalized_ids;
const auto lib_alias = child_chunk.header<ResTable_staged_alias_header>();
if (!lib_alias) {
LOG(ERROR) << "RES_TABLE_STAGED_ALIAS_TYPE is too small.";
@@ -705,8 +707,11 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
const auto entry_begin = child_chunk.data_ptr().convert<ResTable_staged_alias_entry>();
const auto entry_end = entry_begin + dtohl(lib_alias->count);
+ std::unordered_set<uint32_t> finalized_ids;
+ finalized_ids.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??";
return {};
}
auto finalized_id = dtohl(entry_iter->finalizedResId);
@@ -717,8 +722,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
auto staged_id = dtohl(entry_iter->stagedResId);
- auto [_, success] = loaded_package->alias_id_map_.insert(std::make_pair(staged_id,
- finalized_id));
+ auto [_, success] = loaded_package->alias_id_map_.emplace(staged_id, finalized_id);
if (!success) {
LOG(ERROR) << StringPrintf("Repeated staged resource id '%08x' in staged aliases.",
staged_id);
diff --git a/libs/androidfw/Locale.cpp b/libs/androidfw/Locale.cpp
index 3eedda88fdce..d87a3ce72177 100644
--- a/libs/androidfw/Locale.cpp
+++ b/libs/androidfw/Locale.cpp
@@ -29,40 +29,33 @@ using ::android::StringPiece;
namespace android {
-void LocaleValue::set_language(const char* language_chars) {
+template <size_t N, class Transformer>
+static void safe_transform_copy(const char* source, char (&dest)[N], Transformer t) {
size_t i = 0;
- while ((*language_chars) != '\0') {
- language[i++] = ::tolower(*language_chars);
- language_chars++;
+ while (i < N && (*source) != '\0') {
+ dest[i++] = t(i, *source);
+ source++;
+ }
+ while (i < N) {
+ dest[i++] = '\0';
}
}
+void LocaleValue::set_language(const char* language_chars) {
+ safe_transform_copy(language_chars, language, [](size_t, char c) { return ::tolower(c); });
+}
+
void LocaleValue::set_region(const char* region_chars) {
- size_t i = 0;
- while ((*region_chars) != '\0') {
- region[i++] = ::toupper(*region_chars);
- region_chars++;
- }
+ safe_transform_copy(region_chars, region, [](size_t, char c) { return ::toupper(c); });
}
void LocaleValue::set_script(const char* script_chars) {
- size_t i = 0;
- while ((*script_chars) != '\0') {
- if (i == 0) {
- script[i++] = ::toupper(*script_chars);
- } else {
- script[i++] = ::tolower(*script_chars);
- }
- script_chars++;
- }
+ safe_transform_copy(script_chars, script,
+ [](size_t i, char c) { return i ? ::tolower(c) : ::toupper(c); });
}
void LocaleValue::set_variant(const char* variant_chars) {
- size_t i = 0;
- while ((*variant_chars) != '\0') {
- variant[i++] = *variant_chars;
- variant_chars++;
- }
+ safe_transform_copy(variant_chars, variant, [](size_t, char c) { return c; });
}
static inline bool is_alpha(const std::string& str) {
@@ -234,6 +227,10 @@ ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter,
return static_cast<ssize_t>(iter - start_iter);
}
+// Make sure the following memcpy's are properly sized.
+static_assert(sizeof(ResTable_config::localeScript) == sizeof(LocaleValue::script));
+static_assert(sizeof(ResTable_config::localeVariant) == sizeof(LocaleValue::variant));
+
void LocaleValue::InitFromResTable(const ResTable_config& config) {
config.unpackLanguage(language);
config.unpackRegion(region);
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index cae2d0bc16b3..5e8a623d4205 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2677,30 +2677,27 @@ bool ResTable_config::isBetterThan(const ResTable_config& o,
// DENSITY_ANY is now dealt with. We should look to
// pick a density bucket and potentially scale it.
// Any density is potentially useful
- // because the system will scale it. Scaling down
- // is generally better than scaling up.
+ // because the system will scale it. Always prefer
+ // scaling down.
int h = thisDensity;
int l = otherDensity;
bool bImBigger = true;
if (l > h) {
- int t = h;
- h = l;
- l = t;
+ std::swap(l, h);
bImBigger = false;
}
- if (requestedDensity >= h) {
- // requested value higher than both l and h, give h
+ if (h == requestedDensity) {
+ // This handles the case where l == h == requestedDensity.
+ // In that case, this and o are equally good so both
+ // true and false are valid. This preserves previous
+ // behavior.
return bImBigger;
- }
- if (l >= requestedDensity) {
+ } else if (l >= requestedDensity) {
// requested value lower than both l and h, give l
return !bImBigger;
- }
- // saying that scaling down is 2x better than up
- if (((2 * l) - requestedDensity) * h > requestedDensity * requestedDensity) {
- return !bImBigger;
} else {
+ // otherwise give h
return bImBigger;
}
}
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
index 9ebc9969a730..8abe79d01642 100644
--- a/libs/androidfw/TEST_MAPPING
+++ b/libs/androidfw/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "CtsResourcesLoaderTests"
+ },
+ {
+ "name": "libandroidfw_tests"
}
]
}
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 7d01395bbbbc..1bde792da2ba 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -192,6 +192,12 @@ class AssetManager2 {
std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
Asset::AccessMode mode) const;
+ // Returns the resource id of parent style of the specified theme.
+ //
+ // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data
+ // failed.
+ base::expected<uint32_t, NullOrIOError> GetParentThemeResourceId(uint32_t resid) const;
+
// Returns the resource name of the specified resource ID.
//
// Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated.
@@ -486,6 +492,12 @@ class AssetManager2 {
// Steps taken to resolve last resource.
std::vector<Step> steps;
+
+ // The configuration name of the best resource found.
+ String8 best_config_name;
+
+ // The package name of the best resource found.
+ String8 best_package_name;
};
// Record of the last resolved resource's resolution path.
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 3c4ee4e63a76..4394740e44ba 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -766,7 +766,9 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) {
auto result = assetmanager.GetLastResourceResolution();
EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
"\tFor config - de\n"
- "\tFound initial: basic/basic.apk", result);
+ "\tFound initial: basic/basic.apk\n"
+ "Best matching is from default configuration of com.android.basic",
+ result);
}
TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
@@ -787,7 +789,9 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
"\tFor config - de\n"
"\tFound initial: basic/basic.apk\n"
- "\tFound better: basic/basic_de_fr.apk - de", result);
+ "\tFound better: basic/basic_de_fr.apk - de\n"
+ "Best matching is from de configuration of com.android.basic",
+ result);
}
TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
diff --git a/libs/androidfw/tests/Config_test.cpp b/libs/androidfw/tests/Config_test.cpp
index b54915f03c29..698c36f09301 100644
--- a/libs/androidfw/tests/Config_test.cpp
+++ b/libs/androidfw/tests/Config_test.cpp
@@ -75,6 +75,9 @@ TEST(ConfigTest, shouldSelectBestDensity) {
configs.add(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20));
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ configs.add(buildDensityConfig(int(ResTable_config::DENSITY_XHIGH) - 1));
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+
expectedBest = buildDensityConfig(ResTable_config::DENSITY_XHIGH);
configs.add(expectedBest);
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
diff --git a/libs/androidfw/tests/PosixUtils_test.cpp b/libs/androidfw/tests/PosixUtils_test.cpp
index c7b3eba1451f..8c49350796ec 100644
--- a/libs/androidfw/tests/PosixUtils_test.cpp
+++ b/libs/androidfw/tests/PosixUtils_test.cpp
@@ -30,14 +30,14 @@ TEST(PosixUtilsTest, AbsolutePathToBinary) {
const auto result = ExecuteBinary({"/bin/date", "--help"});
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, 0);
- ASSERT_EQ(result->stdout_str.find("usage: date "), 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_EQ(result->stdout_str.find("usage: date "), 0);
+ ASSERT_GE(result->stdout_str.find("usage: date "), 0);
}
TEST(PosixUtilsTest, BadParameters) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 504082db41bb..f6ad4c2f3aa5 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -106,6 +106,8 @@ cc_defaults {
target: {
android: {
shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ "android.hardware.graphics.common@1.2",
"liblog",
"libcutils",
"libutils",
@@ -125,9 +127,11 @@ cc_defaults {
static_libs: [
"libEGL_blobCache",
"libprotoutil",
+ "libshaders",
"libstatslog_hwui",
"libstatspull_lazy",
"libstatssocket_lazy",
+ "libtonemap",
],
},
host: {
@@ -255,7 +259,6 @@ cc_defaults {
"apex/android_bitmap.cpp",
"apex/android_canvas.cpp",
"apex/jni_runtime.cpp",
- "apex/renderthread.cpp",
],
},
host: {
@@ -342,6 +345,7 @@ cc_defaults {
"jni/PathEffect.cpp",
"jni/PathMeasure.cpp",
"jni/Picture.cpp",
+ "jni/Region.cpp",
"jni/Shader.cpp",
"jni/RenderEffect.cpp",
"jni/Typeface.cpp",
@@ -391,7 +395,6 @@ cc_defaults {
"jni/GraphicsStatsService.cpp",
"jni/Movie.cpp",
"jni/MovieImpl.cpp",
- "jni/Region.cpp", // requires libbinder_ndk
"jni/pdf/PdfDocument.cpp",
"jni/pdf/PdfEditor.cpp",
"jni/pdf/PdfRenderer.cpp",
@@ -571,6 +574,7 @@ cc_defaults {
"HardwareBitmapUploader.cpp",
"HWUIProperties.sysprop",
"JankTracker.cpp",
+ "FrameMetricsReporter.cpp",
"Layer.cpp",
"LayerUpdateQueue.cpp",
"ProfileData.cpp",
@@ -676,6 +680,7 @@ cc_test {
"tests/unit/FatVectorTests.cpp",
"tests/unit/GraphicsStatsServiceTests.cpp",
"tests/unit/JankTrackerTests.cpp",
+ "tests/unit/FrameMetricsReporterTests.cpp",
"tests/unit/LayerUpdateQueueTests.cpp",
"tests/unit/LinearAllocatorTests.cpp",
"tests/unit/MatrixTests.cpp",
diff --git a/libs/hwui/ColorMode.h b/libs/hwui/ColorMode.h
index 6d387f9ef43d..3df5c3c9caed 100644
--- a/libs/hwui/ColorMode.h
+++ b/libs/hwui/ColorMode.h
@@ -29,6 +29,8 @@ enum class ColorMode {
Hdr = 2,
// HDR Rec2020 + 1010102
Hdr10 = 3,
+ // Alpha 8
+ A8 = 4,
};
} // namespace android::uirenderer
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 8d112d1c64bf..a5c0924579eb 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -20,9 +20,11 @@
// TODO: Use public SurfaceTexture APIs once available and include public NDK header file instead.
#include <surfacetexture/surface_texture_platform.h>
+
#include "AutoBackendTextureRelease.h"
#include "Matrix.h"
#include "Properties.h"
+#include "android/hdr_metadata.h"
#include "renderstate/RenderState.h"
#include "renderthread/EglManager.h"
#include "renderthread/RenderThread.h"
@@ -147,14 +149,20 @@ void DeferredLayerUpdater::apply() {
mUpdateTexImage = false;
float transformMatrix[16];
android_dataspace dataspace;
+ AHdrMetadataType hdrMetadataType;
+ android_cta861_3_metadata cta861_3;
+ android_smpte2086_metadata smpte2086;
int slot;
bool newContent = false;
+ ARect currentCrop;
+ uint32_t outTransform;
// Note: ASurfaceTexture_dequeueBuffer discards all but the last frame. This
// is necessary if the SurfaceTexture queue is in synchronous mode, and we
// cannot tell which mode it is in.
AHardwareBuffer* hardwareBuffer = ASurfaceTexture_dequeueBuffer(
- mSurfaceTexture.get(), &slot, &dataspace, transformMatrix, &newContent,
- createReleaseFence, fenceWait, this);
+ mSurfaceTexture.get(), &slot, &dataspace, &hdrMetadataType, &cta861_3,
+ &smpte2086, transformMatrix, &outTransform, &newContent, createReleaseFence,
+ fenceWait, this, &currentCrop);
if (hardwareBuffer) {
mCurrentSlot = slot;
@@ -165,12 +173,24 @@ void DeferredLayerUpdater::apply() {
// (invoked by createIfNeeded) will add a ref to the AHardwareBuffer.
AHardwareBuffer_release(hardwareBuffer);
if (layerImage.get()) {
- SkMatrix textureTransform;
- mat4(transformMatrix).copyTo(textureTransform);
// force filtration if buffer size != layer size
bool forceFilter =
mWidth != layerImage->width() || mHeight != layerImage->height();
- updateLayer(forceFilter, textureTransform, layerImage);
+ SkRect currentCropRect =
+ SkRect::MakeLTRB(currentCrop.left, currentCrop.top, currentCrop.right,
+ currentCrop.bottom);
+
+ float maxLuminanceNits = -1.f;
+ if (hdrMetadataType & HDR10_SMPTE2086) {
+ maxLuminanceNits = std::max(smpte2086.maxLuminance, maxLuminanceNits);
+ }
+
+ if (hdrMetadataType & HDR10_CTA861_3) {
+ maxLuminanceNits =
+ std::max(cta861_3.maxContentLightLevel, maxLuminanceNits);
+ }
+ updateLayer(forceFilter, layerImage, outTransform, currentCropRect,
+ maxLuminanceNits);
}
}
}
@@ -182,13 +202,16 @@ void DeferredLayerUpdater::apply() {
}
}
-void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform,
- const sk_sp<SkImage>& layerImage) {
+void DeferredLayerUpdater::updateLayer(bool forceFilter, const sk_sp<SkImage>& layerImage,
+ const uint32_t transform, SkRect currentCrop,
+ float maxLuminanceNits) {
mLayer->setBlend(mBlend);
mLayer->setForceFilter(forceFilter);
mLayer->setSize(mWidth, mHeight);
- mLayer->getTexTransform() = textureTransform;
+ mLayer->setCurrentCropRect(currentCrop);
+ mLayer->setWindowTransform(transform);
mLayer->setImage(layerImage);
+ mLayer->setMaxLuminanceNits(maxLuminanceNits);
}
void DeferredLayerUpdater::detachSurfaceTexture() {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 8f79c4ec97b8..9a4c5505fa35 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -90,8 +90,8 @@ public:
void detachSurfaceTexture();
- void updateLayer(bool forceFilter, const SkMatrix& textureTransform,
- const sk_sp<SkImage>& layerImage);
+ void updateLayer(bool forceFilter, const sk_sp<SkImage>& layerImage, const uint32_t transform,
+ SkRect currentCrop, float maxLuminanceNits = -1.f);
void destroyLayer();
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index fb3e21fc1571..4ec782f6fec0 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -27,6 +27,7 @@ X(ClipPath)
X(ClipRect)
X(ClipRRect)
X(ClipRegion)
+X(ResetClip)
X(DrawPaint)
X(DrawBehind)
X(DrawPath)
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index ef1f5aabcbd8..3ea49518eecd 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -26,6 +26,13 @@ public:
virtual void notify(const int64_t* buffer) = 0;
bool waitForPresentTime() const { return mWaitForPresentTime; };
+ void reportMetricsFrom(uint64_t frameNumber, int32_t surfaceControlId) {
+ mAttachedFrameNumber = frameNumber;
+ mSurfaceControlId = surfaceControlId;
+ };
+ uint64_t attachedFrameNumber() const { return mAttachedFrameNumber; };
+ int32_t attachedSurfaceControlId() const { return mSurfaceControlId; };
+
/**
* Create a new metrics observer. An observer that watches present time gets notified at a
* different time than the observer that doesn't.
@@ -38,10 +45,29 @@ public:
* WARNING! This observer may not receive metrics for the last several frames that the app
* produces.
*/
- FrameMetricsObserver(bool waitForPresentTime) : mWaitForPresentTime(waitForPresentTime) {}
+ FrameMetricsObserver(bool waitForPresentTime)
+ : mWaitForPresentTime(waitForPresentTime)
+ , mSurfaceControlId(INT32_MAX)
+ , mAttachedFrameNumber(UINT64_MAX) {}
private:
const bool mWaitForPresentTime;
+
+ // The id of the surface control (mSurfaceControlGenerationId in CanvasContext)
+ // for which the mAttachedFrameNumber applies to. We rely on this value being
+ // an increasing counter. We will report metrics:
+ // - for all frames if the frame comes from a surface with a surfaceControlId
+ // that is strictly greater than mSurfaceControlId.
+ // - for all frames with a frame number greater than or equal to mAttachedFrameNumber
+ // if the frame comes from a surface with a surfaceControlId that is equal to the
+ // mSurfaceControlId.
+ // We will never report metrics if the frame comes from a surface with a surfaceControlId
+ // that is strictly smaller than mSurfaceControlId.
+ int32_t mSurfaceControlId;
+
+ // The frame number the metrics observer was attached on. Metrics will be sent from this frame
+ // number (inclusive) onwards in the case that the surface id is equal to mSurfaceControlId.
+ uint64_t mAttachedFrameNumber;
};
} // namespace uirenderer
diff --git a/libs/hwui/FrameMetricsReporter.cpp b/libs/hwui/FrameMetricsReporter.cpp
new file mode 100644
index 000000000000..ee32ea17bfaf
--- /dev/null
+++ b/libs/hwui/FrameMetricsReporter.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#include "FrameMetricsReporter.h"
+
+namespace android {
+namespace uirenderer {
+
+void FrameMetricsReporter::reportFrameMetrics(const int64_t* stats, bool hasPresentTime,
+ uint64_t frameNumber, int32_t surfaceControlId) {
+ FatVector<sp<FrameMetricsObserver>, 10> copy;
+ {
+ std::lock_guard lock(mObserversLock);
+ copy.reserve(mObservers.size());
+ for (size_t i = 0; i < mObservers.size(); i++) {
+ auto observer = mObservers[i];
+
+ if (CC_UNLIKELY(surfaceControlId < observer->attachedSurfaceControlId())) {
+ // Don't notify if the metrics are from a frame that was run on an old
+ // surface (one from before the observer was attached).
+ ALOGV("skipped reporting metrics from old surface %d", surfaceControlId);
+ continue;
+ } else if (CC_UNLIKELY(surfaceControlId == observer->attachedSurfaceControlId() &&
+ frameNumber < observer->attachedFrameNumber())) {
+ // Don't notify if the metrics are from a frame that was queued by the
+ // BufferQueueProducer on the render thread before the observer was attached.
+ ALOGV("skipped reporting metrics from old frame %ld", (long)frameNumber);
+ continue;
+ }
+
+ const bool wantsPresentTime = observer->waitForPresentTime();
+ if (hasPresentTime == wantsPresentTime) {
+ copy.push_back(observer);
+ }
+ }
+ }
+ for (size_t i = 0; i < copy.size(); i++) {
+ copy[i]->notify(stats);
+ }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h
index 0ac025fb01db..7e51df7ce6fc 100644
--- a/libs/hwui/FrameMetricsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -63,23 +63,14 @@ public:
* If an observer does not want present time, only notify when 'hasPresentTime' is false.
* Never notify both types of observers from the same callback, because the callback with
* 'hasPresentTime' is sent at a different time than the one without.
+ *
+ * The 'frameNumber' and 'surfaceControlId' associated to the frame whose's stats are being
+ * reported are used to determine whether or not the stats should be reported. We won't report
+ * stats of frames that are from "old" surfaces (i.e. with surfaceControlIds older than the one
+ * the observer was attached on) nor those that are from "old" frame numbers.
*/
- void reportFrameMetrics(const int64_t* stats, bool hasPresentTime) {
- FatVector<sp<FrameMetricsObserver>, 10> copy;
- {
- std::lock_guard lock(mObserversLock);
- copy.reserve(mObservers.size());
- for (size_t i = 0; i < mObservers.size(); i++) {
- const bool wantsPresentTime = mObservers[i]->waitForPresentTime();
- if (hasPresentTime == wantsPresentTime) {
- copy.push_back(mObservers[i]);
- }
- }
- }
- for (size_t i = 0; i < copy.size(); i++) {
- copy[i]->notify(stats);
- }
- }
+ void reportFrameMetrics(const int64_t* stats, bool hasPresentTime, uint64_t frameNumber,
+ int32_t surfaceControlId);
private:
FatVector<sp<FrameMetricsObserver>, 10> mObservers GUARDED_BY(mObserversLock);
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index db3a1081e32c..dd272cd5ff7d 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -287,29 +287,29 @@ private:
std::mutex mVkLock;
};
-bool HardwareBitmapUploader::hasFP16Support() {
- static std::once_flag sOnce;
- static bool hasFP16Support = false;
-
- // Gralloc shouldn't let us create a USAGE_HW_TEXTURE if GLES is unable to consume it, so
- // we don't need to double-check the GLES version/extension.
- std::call_once(sOnce, []() {
- AHardwareBuffer_Desc desc = {
- .width = 1,
- .height = 1,
- .layers = 1,
- .format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
- .usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER |
- AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE,
- };
- UniqueAHardwareBuffer buffer = allocateAHardwareBuffer(desc);
- hasFP16Support = buffer != nullptr;
- });
+static bool checkSupport(AHardwareBuffer_Format format) {
+ AHardwareBuffer_Desc desc = {
+ .width = 1,
+ .height = 1,
+ .layers = 1,
+ .format = format,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER |
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE,
+ };
+ UniqueAHardwareBuffer buffer = allocateAHardwareBuffer(desc);
+ return buffer != nullptr;
+}
+bool HardwareBitmapUploader::hasFP16Support() {
+ static bool hasFP16Support = checkSupport(AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT);
return hasFP16Support;
}
+bool HardwareBitmapUploader::has1010102Support() {
+ static bool has101012Support = checkSupport(AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM);
+ return has101012Support;
+}
+
static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) {
FormatInfo formatInfo;
switch (skBitmap.info().colorType()) {
@@ -350,6 +350,19 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) {
formatInfo.type = GL_UNSIGNED_BYTE;
formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
break;
+ case kRGBA_1010102_SkColorType:
+ formatInfo.isSupported = HardwareBitmapUploader::has1010102Support();
+ if (formatInfo.isSupported) {
+ formatInfo.type = GL_UNSIGNED_INT_2_10_10_10_REV;
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
+ formatInfo.vkFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
+ } else {
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+ formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ }
+ formatInfo.format = GL_RGBA;
+ break;
default:
ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
formatInfo.valid = false;
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index ad7a95a4fa03..34f43cd8a198 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -29,10 +29,12 @@ public:
#ifdef __ANDROID__
static bool hasFP16Support();
+ static bool has1010102Support();
#else
static bool hasFP16Support() {
return true;
}
+ static bool has1010102Support() { return true; }
#endif
};
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 34e5577066f9..1e5be6c3eed7 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -111,19 +111,22 @@ void JankTracker::calculateLegacyJank(FrameInfo& frame) REQUIRES(mDataMutex) {
// the actual time spent blocked.
nsecs_t forgiveAmount =
std::min(expectedDequeueDuration, frame[FrameInfoIndex::DequeueBufferDuration]);
- LOG_ALWAYS_FATAL_IF(forgiveAmount >= totalDuration,
- "Impossible dequeue duration! dequeue duration reported %" PRId64
- ", total duration %" PRId64,
- forgiveAmount, totalDuration);
+ if (forgiveAmount >= totalDuration) {
+ ALOGV("Impossible dequeue duration! dequeue duration reported %" PRId64
+ ", total duration %" PRId64,
+ forgiveAmount, totalDuration);
+ return;
+ }
totalDuration -= forgiveAmount;
}
}
- LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64 " start=%" PRIi64
- " gpuComplete=%" PRIi64, totalDuration,
- frame[FrameInfoIndex::IntendedVsync],
- frame[FrameInfoIndex::GpuCompleted]);
-
+ if (totalDuration <= 0) {
+ ALOGV("Impossible totalDuration %" PRId64 " start=%" PRIi64 " gpuComplete=%" PRIi64,
+ totalDuration, frame[FrameInfoIndex::IntendedVsync],
+ frame[FrameInfoIndex::GpuCompleted]);
+ return;
+ }
// Only things like Surface.lockHardwareCanvas() are exempt from tracking
if (CC_UNLIKELY(frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS)) {
@@ -164,7 +167,8 @@ void JankTracker::calculateLegacyJank(FrameInfo& frame) REQUIRES(mDataMutex) {
- lastFrameOffset + mFrameIntervalLegacy;
}
-void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter) {
+void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter,
+ int64_t frameNumber, int32_t surfaceControlId) {
std::lock_guard lock(mDataMutex);
calculateLegacyJank(frame);
@@ -173,7 +177,10 @@ void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsRepo
int64_t totalDuration = frame.duration(FrameInfoIndex::IntendedVsync,
FrameInfoIndex::FrameCompleted);
- LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration);
+ if (totalDuration <= 0) {
+ ALOGV("Impossible totalDuration %" PRId64, totalDuration);
+ return;
+ }
mData->reportFrame(totalDuration);
(*mGlobalData)->reportFrame(totalDuration);
@@ -253,7 +260,8 @@ void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsRepo
}
if (CC_UNLIKELY(reporter.get() != nullptr)) {
- reporter->reportFrameMetrics(frame.data(), false /* hasPresentTime */);
+ reporter->reportFrameMetrics(frame.data(), false /* hasPresentTime */, frameNumber,
+ surfaceControlId);
}
}
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index bdb784dc8747..bcd031efa78d 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -57,7 +57,8 @@ public:
}
FrameInfo* startFrame() { return &mFrames.next(); }
- void finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter);
+ void finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter,
+ int64_t frameNumber, int32_t surfaceId);
// Calculates the 'legacy' jank information, i.e. with outdated refresh rate information and
// without GPU completion or deadlined information.
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index b14ade97ca5f..9053c1240957 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,6 +20,8 @@
#include "utils/Color.h"
#include "utils/MathUtils.h"
+#include <log/log.h>
+
namespace android {
namespace uirenderer {
@@ -33,7 +35,6 @@ Layer::Layer(RenderState& renderState, sk_sp<SkColorFilter> colorFilter, int alp
// preserves the old inc/dec ref locations. This should be changed...
incStrong(nullptr);
renderState.registerLayer(this);
- texTransform.setIdentity();
transform.setIdentity();
}
@@ -90,7 +91,7 @@ static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, cons
void Layer::draw(SkCanvas* canvas) {
GrRecordingContext* context = canvas->recordingContext();
if (context == nullptr) {
- SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
+ ALOGD("Attempting to draw LayerDrawable into an unsupported surface");
return;
}
SkMatrix layerTransform = getTransform();
@@ -99,7 +100,6 @@ void Layer::draw(SkCanvas* canvas) {
const int layerHeight = getHeight();
if (layerImage) {
SkMatrix textureMatrixInv;
- textureMatrixInv = getTexTransform();
// TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
// use bottom left origin and remove flipV and invert transformations.
SkMatrix flipV;
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e99e76299317..47eb5d3bfb83 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -74,10 +74,18 @@ public:
void setColorFilter(sk_sp<SkColorFilter> filter) { mColorFilter = filter; };
- inline SkMatrix& getTexTransform() { return texTransform; }
-
inline SkMatrix& getTransform() { return transform; }
+ inline SkRect getCurrentCropRect() { return mCurrentCropRect; }
+
+ inline void setCurrentCropRect(const SkRect currentCropRect) {
+ mCurrentCropRect = currentCropRect;
+ }
+
+ inline void setWindowTransform(uint32_t windowTransform) { mWindowTransform = windowTransform; }
+
+ inline uint32_t getWindowTransform() { return mWindowTransform; }
+
/**
* Posts a decStrong call to the appropriate thread.
* Thread-safe.
@@ -88,6 +96,12 @@ public:
inline sk_sp<SkImage> getImage() const { return this->layerImage; }
+ inline void setMaxLuminanceNits(float maxLuminanceNits) {
+ mMaxLuminanceNits = maxLuminanceNits;
+ }
+
+ inline float getMaxLuminanceNits() { return mMaxLuminanceNits; }
+
void draw(SkCanvas* canvas);
protected:
@@ -116,14 +130,19 @@ private:
SkBlendMode mode;
/**
- * Optional texture coordinates transform.
+ * Optional transform.
*/
- SkMatrix texTransform;
+ SkMatrix transform;
/**
- * Optional transform.
+ * Optional crop
*/
- SkMatrix transform;
+ SkRect mCurrentCropRect;
+
+ /**
+ * Optional transform
+ */
+ uint32_t mWindowTransform;
/**
* An image backing the layer.
@@ -145,6 +164,11 @@ private:
*/
bool mBlend = false;
+ /**
+ * Max input luminance if the layer is HDR
+ */
+ float mMaxLuminanceNits = -1;
+
}; // struct Layer
} // namespace uirenderer
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 2eb2c7c7e299..e16fd8c38c75 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -88,14 +88,10 @@ public:
bool getShouldClip() const { return mShouldClip; }
- bool willClip() const {
- // only round rect outlines can be used for clipping
- return mShouldClip && (mType == Type::RoundRect);
- }
+ bool willClip() const { return mShouldClip; }
- bool willRoundRectClip() const {
- // only round rect outlines can be used for clipping
- return willClip() && MathUtils::isPositive(mRadius);
+ bool willComplexClip() const {
+ return mShouldClip && (mType != Type::RoundRect || MathUtils::isPositive(mRadius));
}
bool getAsRoundRect(Rect* outRect, float* outRadius) const {
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index dd8439647fd3..3d0ca0a10851 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -137,6 +137,7 @@ void ProfileData::dump(int fd) const {
histogramGPUForEach([fd](HistogramEntry entry) {
dprintf(fd, " %ums=%u", entry.renderTimeMs, entry.frameCount);
});
+ dprintf(fd, "\n");
}
uint32_t ProfileData::findPercentile(int percentile) const {
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index a743d30939d0..4cce87ad1a2f 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -243,18 +243,14 @@ CopyResult Readback::copySurfaceIntoLegacy(ANativeWindow* window, const Rect& sr
static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
sk_sp<SkImage> image =
SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
- return copyImageInto(image, texTransform, srcRect, bitmap);
+ return copyImageInto(image, srcRect, bitmap);
}
CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
LOG_ALWAYS_FATAL_IF(!hwBitmap->isHardware());
Rect srcRect;
- Matrix4 transform;
- transform.loadScale(1, -1, 1);
- transform.translate(0, -1);
-
- return copyImageInto(hwBitmap->makeImage(), transform, srcRect, bitmap);
+ return copyImageInto(hwBitmap->makeImage(), srcRect, bitmap);
}
CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
@@ -279,14 +275,11 @@ CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap
CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap) {
Rect srcRect;
- Matrix4 transform;
- transform.loadScale(1, -1, 1);
- transform.translate(0, -1);
- return copyImageInto(image, transform, srcRect, bitmap);
+ return copyImageInto(image, srcRect, bitmap);
}
-CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
- const Rect& srcRect, SkBitmap* bitmap) {
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
+ SkBitmap* bitmap) {
ATRACE_CALL();
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
mRenderThread.requireGlContext();
@@ -303,11 +296,6 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran
CopyResult copyResult = CopyResult::UnknownError;
int displayedWidth = imgWidth, displayedHeight = imgHeight;
- // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
- // size.
- if (texTransform[Matrix4::kSkewX] >= 0.5f || texTransform[Matrix4::kSkewX] <= -0.5f) {
- std::swap(displayedWidth, displayedHeight);
- }
SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
SkRect skiaSrcRect = srcRect.toSkRect();
if (skiaSrcRect.isEmpty()) {
@@ -320,7 +308,6 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran
Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc);
layer.setSize(displayedWidth, displayedHeight);
- texTransform.copyTo(layer.getTexTransform());
layer.setImage(image);
// Scaling filter is not explicitly set here, because it is done inside copyLayerInfo
// after checking the necessity based on the src/dest rect size and the transformation.
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index da252695dd3b..d0d748ff5c16 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -56,8 +56,7 @@ public:
private:
CopyResult copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
- CopyResult copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
- 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,
SkBitmap* bitmap);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 442ae0fb2707..a285462eef74 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -15,6 +15,7 @@
*/
#include "RecordingCanvas.h"
+#include <hwui/Paint.h>
#include <GrRecordingContext.h>
@@ -186,6 +187,11 @@ struct ClipRegion final : Op {
SkClipOp op;
void draw(SkCanvas* c, const SkMatrix&) const { c->clipRegion(region, op); }
};
+struct ResetClip final : Op {
+ static const auto kType = Type::ResetClip;
+ ResetClip() {}
+ void draw(SkCanvas* c, const SkMatrix&) const { SkAndroidFrameworkUtils::ResetClip(c); }
+};
struct DrawPaint final : Op {
static const auto kType = Type::DrawPaint;
@@ -495,7 +501,7 @@ struct DrawVectorDrawable final : Op {
sp<VectorDrawableRoot> mRoot;
SkRect mBounds;
- SkPaint paint;
+ Paint paint;
BitmapPalette palette;
};
@@ -661,6 +667,9 @@ void DisplayListData::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
void DisplayListData::clipRegion(const SkRegion& region, SkClipOp op) {
this->push<ClipRegion>(0, region, op);
}
+void DisplayListData::resetClip() {
+ this->push<ResetClip>(0);
+}
void DisplayListData::drawPaint(const SkPaint& paint) {
this->push<DrawPaint>(0, paint);
@@ -833,7 +842,8 @@ constexpr color_transform_fn colorTransformForOp() {
// TODO: We should be const. Or not. Or just use a different map
// Unclear, but this is the quick fix
const T* op = reinterpret_cast<const T*>(opRaw);
- transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
+ const SkPaint* paint = &op->paint;
+ transformPaint(transform, const_cast<SkPaint*>(paint), op->palette);
};
}
else if
@@ -842,7 +852,8 @@ constexpr color_transform_fn colorTransformForOp() {
// TODO: We should be const. Or not. Or just use a different map
// Unclear, but this is the quick fix
const T* op = reinterpret_cast<const T*>(opRaw);
- transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+ const SkPaint* paint = &op->paint;
+ transformPaint(transform, const_cast<SkPaint*>(paint));
};
}
else {
@@ -966,6 +977,14 @@ void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
fDL->clipRegion(region, op);
this->INHERITED::onClipRegion(region, op);
}
+void RecordingCanvas::onResetClip() {
+ // This is part of "replace op" emulation, but rely on the following intersection
+ // clip to potentially mark the clip as complex. If we are already complex, we do
+ // not reset the complexity so that we don't break the contract that no higher
+ // save point has a complex clip when "not complex".
+ fDL->resetClip();
+ this->INHERITED::onResetClip();
+}
void RecordingCanvas::onDrawPaint(const SkPaint& paint) {
fDL->drawPaint(paint);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 4fae6a13a25a..212b4e72dcb2 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -97,6 +97,7 @@ private:
void clipRect(const SkRect&, SkClipOp, bool aa);
void clipRRect(const SkRRect&, SkClipOp, bool aa);
void clipRegion(const SkRegion&, SkClipOp);
+ void resetClip();
void drawPaint(const SkPaint&);
void drawBehind(const SkPaint&);
@@ -169,6 +170,7 @@ public:
void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
void onClipRegion(const SkRegion&, SkClipOp) override;
+ void onResetClip() override;
void onDrawPaint(const SkPaint&) override;
void onDrawBehind(const SkPaint&) override;
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index cd622eba37b6..064ba7aee107 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -165,11 +165,11 @@ public:
bool prepareForFunctorPresence(bool willHaveFunctor, bool ancestorDictatesFunctorsNeedLayer) {
// parent may have already dictated that a descendant layer is needed
bool functorsNeedLayer =
- ancestorDictatesFunctorsNeedLayer
- || CC_UNLIKELY(isClipMayBeComplex())
+ ancestorDictatesFunctorsNeedLayer ||
+ CC_UNLIKELY(isClipMayBeComplex())
// Round rect clipping forces layer for functors
- || CC_UNLIKELY(getOutline().willRoundRectClip()) ||
+ || CC_UNLIKELY(getOutline().willComplexClip()) ||
CC_UNLIKELY(getRevealClip().willClip())
// Complex matrices forces layer, due to stencil clipping
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d032e2b00649..53c6db0cdf3a 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -182,7 +182,7 @@ int SkiaCanvas::saveUnclippedLayer(int left, int top, int right, int bottom) {
return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds);
}
-void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const SkPaint& paint) {
+void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const Paint& paint) {
while (mCanvas->getSaveCount() > restoreCount + 1) {
this->restore();
@@ -396,6 +396,22 @@ bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) {
return !mCanvas->isClipEmpty();
}
+bool SkiaCanvas::replaceClipRect_deprecated(float left, float top, float right, float bottom) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+
+ // Emulated clip rects are not recorded for partial saves, since
+ // partial saves have been removed from the public API.
+ SkAndroidFrameworkUtils::ResetClip(mCanvas);
+ mCanvas->clipRect(rect, SkClipOp::kIntersect);
+ return !mCanvas->isClipEmpty();
+}
+
+bool SkiaCanvas::replaceClipPath_deprecated(const SkPath* path) {
+ SkAndroidFrameworkUtils::ResetClip(mCanvas);
+ mCanvas->clipPath(*path, SkClipOp::kIntersect, true);
+ return !mCanvas->isClipEmpty();
+}
+
// ----------------------------------------------------------------------------
// Canvas state operations: Filters
// ----------------------------------------------------------------------------
@@ -439,13 +455,13 @@ void SkiaCanvas::drawColor(int color, SkBlendMode mode) {
mCanvas->drawColor(color, mode);
}
-void SkiaCanvas::onFilterPaint(SkPaint& paint) {
+void SkiaCanvas::onFilterPaint(Paint& paint) {
if (mPaintFilter) {
- mPaintFilter->filter(&paint);
+ mPaintFilter->filterFullPaint(&paint);
}
}
-void SkiaCanvas::drawPaint(const SkPaint& paint) {
+void SkiaCanvas::drawPaint(const Paint& paint) {
mCanvas->drawPaint(filterPaint(paint));
}
@@ -552,9 +568,8 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
auto image = bitmap.makeImage();
- applyLooper(paint, [&](const SkPaint& p) {
- auto sampling = SkSamplingOptions(p.getFilterQuality());
- mCanvas->drawImage(image, left, top, sampling, &p);
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImage(image, left, top, p.sampling(), &p);
});
}
@@ -562,9 +577,8 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint*
auto image = bitmap.makeImage();
SkAutoCanvasRestore acr(mCanvas, true);
mCanvas->concat(matrix);
- applyLooper(paint, [&](const SkPaint& p) {
- auto sampling = SkSamplingOptions(p.getFilterQuality());
- mCanvas->drawImage(image, 0, 0, sampling, &p);
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImage(image, 0, 0, p.sampling(), &p);
});
}
@@ -575,18 +589,12 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float s
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- applyLooper(paint, [&](const SkPaint& p) {
- auto sampling = SkSamplingOptions(p.getFilterQuality());
- mCanvas->drawImageRect(image, srcRect, dstRect, sampling, &p,
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImageRect(image, srcRect, dstRect, p.sampling(), &p,
SkCanvas::kFast_SrcRectConstraint);
});
}
-static SkFilterMode paintToFilter(const Paint* paint) {
- return paint && paint->isFilterBitmap() ? SkFilterMode::kLinear
- : SkFilterMode::kNearest;
-}
-
void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const Paint* paint) {
const int ptCount = (meshWidth + 1) * (meshHeight + 1);
@@ -668,13 +676,13 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
if (paint) {
pnt = *paint;
}
- SkSamplingOptions sampling(paintToFilter(&pnt));
+ SkSamplingOptions sampling = pnt.sampling();
pnt.setShader(image->makeShader(sampling));
auto v = builder.detach();
- applyLooper(&pnt, [&](const SkPaint& p) {
+ applyLooper(&pnt, [&](const Paint& p) {
SkPaint copy(p);
- auto s = SkSamplingOptions(p.getFilterQuality());
+ auto s = p.sampling();
if (s != sampling) {
// applyLooper changed the quality?
copy.setShader(image->makeShader(s));
@@ -707,9 +715,8 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
auto image = bitmap.makeImage();
- applyLooper(paint, [&](const SkPaint& p) {
- auto filter = SkSamplingOptions(p.getFilterQuality()).filter;
- mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p);
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImageLattice(image.get(), lattice, dst, p.filterMode(), &p);
});
}
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 438a40cb4c81..715007cdcd3b 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -23,8 +23,10 @@
#include "VectorDrawable.h"
#include "hwui/Canvas.h"
#include "hwui/Paint.h"
+#include "hwui/BlurDrawLooper.h"
#include <SkCanvas.h>
+#include <SkDeque.h>
#include "pipeline/skia/AnimatedDrawables.h"
#include "src/core/SkArenaAlloc.h"
@@ -73,7 +75,7 @@ public:
virtual int save(SaveFlags::Flags flags) override;
virtual void restore() override;
virtual void restoreToCount(int saveCount) override;
- virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) override;
+ virtual void restoreUnclippedLayer(int saveCount, const Paint& paint) override;
virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint) override;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha) override;
@@ -92,6 +94,9 @@ public:
virtual bool quickRejectPath(const SkPath& path) const override;
virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override;
virtual bool clipPath(const SkPath* path, SkClipOp op) override;
+ virtual bool replaceClipRect_deprecated(float left, float top, float right,
+ float bottom) override;
+ virtual bool replaceClipPath_deprecated(const SkPath* path) override;
virtual PaintFilter* getPaintFilter() override;
virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) override;
@@ -99,7 +104,7 @@ public:
virtual SkCanvasState* captureCanvasState() const override;
virtual void drawColor(int color, SkBlendMode mode) override;
- virtual void drawPaint(const SkPaint& paint) override;
+ virtual void drawPaint(const Paint& paint) override;
virtual void drawPoint(float x, float y, const Paint& paint) override;
virtual void drawPoints(const float* points, int count, const Paint& paint) override;
@@ -167,10 +172,10 @@ protected:
const Paint& paint, const SkPath& path, size_t start,
size_t end) override;
- void onFilterPaint(SkPaint& paint);
+ void onFilterPaint(Paint& paint);
- SkPaint filterPaint(const SkPaint& src) {
- SkPaint dst(src);
+ Paint filterPaint(const Paint& src) {
+ Paint dst(src);
this->onFilterPaint(dst);
return dst;
}
@@ -179,21 +184,20 @@ protected:
template <typename Proc>
void applyLooper(const Paint* paint, Proc proc, void (*preFilter)(SkPaint&) = nullptr) {
BlurDrawLooper* looper = paint ? paint->getLooper() : nullptr;
- const SkPaint* skpPtr = paint;
- SkPaint skp = skpPtr ? *skpPtr : SkPaint();
+ Paint pnt = paint ? *paint : Paint();
if (preFilter) {
- preFilter(skp);
+ preFilter(pnt);
}
- this->onFilterPaint(skp);
+ this->onFilterPaint(pnt);
if (looper) {
- looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) {
+ looper->apply(pnt, [&](SkPoint offset, const Paint& modifiedPaint) {
mCanvas->save();
mCanvas->translate(offset.fX, offset.fY);
proc(modifiedPaint);
mCanvas->restore();
});
} else {
- proc(skp);
+ proc(pnt);
}
}
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 55f434f49bbd..983c7766273a 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -269,7 +269,7 @@ void FullPath::FullPathProperties::setPropertyValue(int propertyId, float value)
void ClipPath::draw(SkCanvas* outCanvas, bool useStagingData) {
SkPath tempStagingPath;
- outCanvas->clipPath(getUpdatedPath(useStagingData, &tempStagingPath));
+ outCanvas->clipPath(getUpdatedPath(useStagingData, &tempStagingPath), true);
}
Group::Group(const Group& group) : Node(group) {
@@ -463,10 +463,10 @@ void Tree::drawStaging(Canvas* outCanvas) {
mStagingCache.dirty = false;
}
- SkPaint skp;
+ Paint skp;
getPaintFor(&skp, mStagingProperties);
Paint paint;
- paint.setFilterQuality(skp.getFilterQuality());
+ paint.setFilterBitmap(skp.isFilterBitmap());
paint.setColorFilter(skp.refColorFilter());
paint.setAlpha(skp.getAlpha());
outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(),
@@ -476,9 +476,9 @@ void Tree::drawStaging(Canvas* outCanvas) {
mStagingProperties.getBounds().bottom(), &paint);
}
-void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties& prop) const {
+void Tree::getPaintFor(Paint* outPaint, const TreeProperties& prop) const {
// HWUI always draws VD with bilinear filtering.
- outPaint->setFilterQuality(kLow_SkFilterQuality);
+ outPaint->setFilterBitmap(true);
if (prop.getColorFilter() != nullptr) {
outPaint->setColorFilter(sk_ref_sp(prop.getColorFilter()));
}
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index ac7d41e0d600..30bb04ae8361 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -648,7 +648,7 @@ public:
*/
void draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& paint);
- void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const;
+ void getPaintFor(Paint* outPaint, const TreeProperties &props) const;
BitmapPalette computePalette();
void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); }
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 5aad821ad59f..6fc251dc815c 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -118,6 +118,24 @@ void WebViewFunctor::onRemovedFromTree() {
}
}
+bool WebViewFunctor::prepareRootSurfaceControl() {
+ if (!Properties::enableWebViewOverlays) return false;
+
+ renderthread::CanvasContext* activeContext = renderthread::CanvasContext::getActiveContext();
+ if (!activeContext) return false;
+
+ ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
+ if (!rootSurfaceControl) return false;
+
+ int32_t rgid = activeContext->getSurfaceControlGenerationId();
+ if (mParentSurfaceControlGenerationId != rgid) {
+ reparentSurfaceControl(rootSurfaceControl);
+ mParentSurfaceControlGenerationId = rgid;
+ }
+
+ return true;
+}
+
void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
ATRACE_NAME("WebViewFunctor::drawGl");
if (!mHasContext) {
@@ -131,20 +149,8 @@ void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
.mergeTransaction = currentFunctor.mergeTransaction,
};
- if (Properties::enableWebViewOverlays && !drawInfo.isLayer) {
- renderthread::CanvasContext* activeContext =
- renderthread::CanvasContext::getActiveContext();
- if (activeContext != nullptr) {
- ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
- if (rootSurfaceControl) {
- overlayParams.overlaysMode = OverlaysMode::Enabled;
- int32_t rgid = activeContext->getSurfaceControlGenerationId();
- if (mParentSurfaceControlGenerationId != rgid) {
- reparentSurfaceControl(rootSurfaceControl);
- mParentSurfaceControlGenerationId = rgid;
- }
- }
- }
+ if (!drawInfo.isLayer && prepareRootSurfaceControl()) {
+ overlayParams.overlaysMode = OverlaysMode::Enabled;
}
mCallbacks.gles.draw(mFunctor, mData, drawInfo, overlayParams);
@@ -170,7 +176,10 @@ void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) {
.mergeTransaction = currentFunctor.mergeTransaction,
};
- // TODO, enable surface control once offscreen mode figured out
+ if (!params.is_layer && prepareRootSurfaceControl()) {
+ overlayParams.overlaysMode = OverlaysMode::Enabled;
+ }
+
mCallbacks.vk.draw(mFunctor, mData, params, overlayParams);
}
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index f28f310993ec..0a02f2d4b720 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -88,6 +88,7 @@ public:
}
private:
+ bool prepareRootSurfaceControl();
void reparentSurfaceControl(ASurfaceControl* parent);
private:
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index dca10e29cbb8..942c0506321c 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -14,31 +14,22 @@
* limitations under the License.
*/
-#include "graphics_jni_helpers.h"
-
#include <GraphicsJNI.h>
#include <SkGraphics.h>
-#include <sstream>
-#include <iostream>
-#include <unicode/putil.h>
#include <unordered_map>
#include <vector>
-using namespace std;
-
-/*
- * This is responsible for setting up the JNI environment for communication between
- * the Java and native parts of layoutlib, including registering native methods.
- * This is mostly achieved by copying the way it is done in the platform
- * (see AndroidRuntime.cpp).
- */
+#include "Properties.h"
+#include "android/graphics/jni_runtime.h"
+#include "graphics_jni_helpers.h"
-static JavaVM* javaVM;
+using namespace std;
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env);
+extern int register_android_graphics_Camera(JNIEnv* env);
extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_ImageDecoder(JNIEnv*);
@@ -49,10 +40,12 @@ extern int register_android_graphics_PathEffect(JNIEnv* env);
extern int register_android_graphics_Shader(JNIEnv* env);
extern int register_android_graphics_RenderEffect(JNIEnv* env);
extern int register_android_graphics_Typeface(JNIEnv* env);
+extern int register_android_graphics_YuvImage(JNIEnv* env);
namespace android {
extern int register_android_graphics_Canvas(JNIEnv* env);
+extern int register_android_graphics_CanvasProperty(JNIEnv* env);
extern int register_android_graphics_ColorFilter(JNIEnv* env);
extern int register_android_graphics_ColorSpace(JNIEnv* env);
extern int register_android_graphics_DrawFilter(JNIEnv* env);
@@ -62,7 +55,7 @@ extern int register_android_graphics_Paint(JNIEnv* env);
extern int register_android_graphics_Path(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);
+extern int register_android_graphics_Region(JNIEnv* env);
extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env);
extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env);
extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
@@ -71,9 +64,11 @@ extern int register_android_graphics_fonts_Font(JNIEnv* env);
extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
extern int register_android_graphics_text_LineBreaker(JNIEnv* env);
extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
+extern int register_android_graphics_text_TextShaper(JNIEnv* env);
+
extern int register_android_util_PathParser(JNIEnv* env);
-extern int register_android_view_RenderNode(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
+extern int register_android_view_RenderNode(JNIEnv* env);
#define REG_JNI(name) { name }
struct RegJNIRec {
@@ -87,8 +82,9 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
{"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
{"android.graphics.ByteBufferStreamAdaptor",
REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
+ {"android.graphics.Camera", REG_JNI(register_android_graphics_Camera)},
{"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
- {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
+ {"android.graphics.CanvasProperty", REG_JNI(register_android_graphics_CanvasProperty)},
{"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
{"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
{"android.graphics.CreateJavaOutputStreamAdaptor",
@@ -107,10 +103,12 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
{"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)},
-// {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
+ {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
+ {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
{"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
{"android.graphics.RenderEffect", REG_JNI(register_android_graphics_RenderEffect)},
{"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
+ {"android.graphics.YuvImage", REG_JNI(register_android_graphics_YuvImage)},
{"android.graphics.animation.NativeInterpolatorFactory",
REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)},
{"android.graphics.animation.RenderNodeAnimator",
@@ -124,6 +122,7 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
{"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
{"android.graphics.text.MeasuredText",
REG_JNI(register_android_graphics_text_MeasuredText)},
+ {"android.graphics.text.TextRunShaper", REG_JNI(register_android_graphics_text_TextShaper)},
{"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
};
@@ -177,10 +176,9 @@ int register_android_graphics_classes(JNIEnv *env) {
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
// Get the names of classes that need to register their native methods
- auto nativesClassesJString =
- (jstring) env->CallStaticObjectMethod(system,
- getPropertyMethod, env->NewStringUTF("native_classes"),
- env->NewStringUTF(""));
+ auto nativesClassesJString = (jstring)env->CallStaticObjectMethod(
+ system, getPropertyMethod, env->NewStringUTF("graphics_native_classes"),
+ env->NewStringUTF(""));
vector<string> classesToRegister = parseCsv(env, nativesClassesJString);
if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) {
diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp
index 3780ba072308..bc6bc456ba5a 100644
--- a/libs/hwui/apex/android_bitmap.cpp
+++ b/libs/hwui/apex/android_bitmap.cpp
@@ -57,6 +57,8 @@ static AndroidBitmapFormat getFormat(const SkImageInfo& info) {
return ANDROID_BITMAP_FORMAT_A_8;
case kRGBA_F16_SkColorType:
return ANDROID_BITMAP_FORMAT_RGBA_F16;
+ case kRGBA_1010102_SkColorType:
+ return ANDROID_BITMAP_FORMAT_RGBA_1010102;
default:
return ANDROID_BITMAP_FORMAT_NONE;
}
@@ -74,6 +76,8 @@ static SkColorType getColorType(AndroidBitmapFormat format) {
return kAlpha_8_SkColorType;
case ANDROID_BITMAP_FORMAT_RGBA_F16:
return kRGBA_F16_SkColorType;
+ case ANDROID_BITMAP_FORMAT_RGBA_1010102:
+ return kRGBA_1010102_SkColorType;
default:
return kUnknown_SkColorType;
}
@@ -249,6 +253,9 @@ int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const
case ANDROID_BITMAP_FORMAT_RGBA_F16:
colorType = kRGBA_F16_SkColorType;
break;
+ case ANDROID_BITMAP_FORMAT_RGBA_1010102:
+ colorType = kRGBA_1010102_SkColorType;
+ break;
default:
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
}
diff --git a/libs/hwui/apex/include/android/graphics/renderthread.h b/libs/hwui/apex/include/android/graphics/renderthread.h
deleted file mode 100644
index 50280a6dd1fb..000000000000
--- a/libs/hwui/apex/include/android/graphics/renderthread.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef ANDROID_GRAPHICS_RENDERTHREAD_H
-#define ANDROID_GRAPHICS_RENDERTHREAD_H
-
-#include <cutils/compiler.h>
-#include <sys/cdefs.h>
-
-__BEGIN_DECLS
-
-/**
- * Dumps a textual representation of the graphics stats for this process.
- * @param fd The file descriptor that the available graphics stats will be appended to. The
- * function requires a valid fd, but does not persist or assume ownership of the fd
- * outside the scope of this function.
- */
-ANDROID_API void ARenderThread_dumpGraphicsMemory(int fd);
-
-__END_DECLS
-
-#endif // ANDROID_GRAPHICS_RENDERTHREAD_H
diff --git a/libs/hwui/apex/renderthread.cpp b/libs/hwui/apex/renderthread.cpp
deleted file mode 100644
index 5d26afe7a2ab..000000000000
--- a/libs/hwui/apex/renderthread.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "android/graphics/renderthread.h"
-
-#include <renderthread/RenderProxy.h>
-
-using namespace android;
-
-void ARenderThread_dumpGraphicsMemory(int fd) {
- uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
-}
diff --git a/libs/hwui/canvas/CanvasFrontend.cpp b/libs/hwui/canvas/CanvasFrontend.cpp
index 8f261c83b8d3..112ac124ffa1 100644
--- a/libs/hwui/canvas/CanvasFrontend.cpp
+++ b/libs/hwui/canvas/CanvasFrontend.cpp
@@ -30,44 +30,61 @@ void CanvasStateHelper::resetState(int width, int height) {
mClipStack.clear();
mTransformStack.clear();
mSaveStack.emplace_back();
- mClipStack.emplace_back().setRect(mInitialBounds);
+ mClipStack.emplace_back();
mTransformStack.emplace_back();
- mCurrentClipIndex = 0;
- mCurrentTransformIndex = 0;
+
+ clip().bounds = mInitialBounds;
}
bool CanvasStateHelper::internalSave(SaveEntry saveEntry) {
mSaveStack.push_back(saveEntry);
if (saveEntry.matrix) {
- // We need to push before accessing transform() to ensure the reference doesn't move
- // across vector resizes
- mTransformStack.emplace_back() = transform();
- mCurrentTransformIndex += 1;
+ pushEntry(&mTransformStack);
}
if (saveEntry.clip) {
- // We need to push before accessing clip() to ensure the reference doesn't move
- // across vector resizes
- mClipStack.emplace_back() = clip();
- mCurrentClipIndex += 1;
+ pushEntry(&mClipStack);
return true;
}
return false;
}
-// Assert that the cast from SkClipOp to SkRegion::Op is valid
-static_assert(static_cast<int>(SkClipOp::kDifference) == SkRegion::Op::kDifference_Op);
-static_assert(static_cast<int>(SkClipOp::kIntersect) == SkRegion::Op::kIntersect_Op);
-static_assert(static_cast<int>(SkClipOp::kUnion_deprecated) == SkRegion::Op::kUnion_Op);
-static_assert(static_cast<int>(SkClipOp::kXOR_deprecated) == SkRegion::Op::kXOR_Op);
-static_assert(static_cast<int>(SkClipOp::kReverseDifference_deprecated) == SkRegion::Op::kReverseDifference_Op);
-static_assert(static_cast<int>(SkClipOp::kReplace_deprecated) == SkRegion::Op::kReplace_Op);
+void CanvasStateHelper::ConservativeClip::apply(SkClipOp op, const SkMatrix& matrix,
+ const SkRect& bounds, bool aa, bool fillsBounds) {
+ this->aa |= aa;
+
+ if (op == SkClipOp::kIntersect) {
+ SkRect devBounds;
+ bool rect = matrix.mapRect(&devBounds, bounds) && fillsBounds;
+ if (!this->bounds.intersect(aa ? devBounds.roundOut() : devBounds.round())) {
+ this->bounds.setEmpty();
+ }
+ this->rect &= rect;
+ } else {
+ // Difference operations subtracts a region from the clip, so conservatively
+ // the bounds remain unchanged and the shape is unlikely to remain a rect.
+ this->rect = false;
+ }
+}
void CanvasStateHelper::internalClipRect(const SkRect& rect, SkClipOp op) {
- clip().opRect(rect, transform(), mInitialBounds, (SkRegion::Op)op, false);
+ clip().apply(op, transform(), rect, /*aa=*/false, /*fillsBounds=*/true);
}
void CanvasStateHelper::internalClipPath(const SkPath& path, SkClipOp op) {
- clip().opPath(path, transform(), mInitialBounds, (SkRegion::Op)op, true);
+ SkRect bounds = path.getBounds();
+ if (path.isInverseFillType()) {
+ // Toggle op type if the path is inverse filled
+ op = (op == SkClipOp::kIntersect ? SkClipOp::kDifference : SkClipOp::kIntersect);
+ }
+ clip().apply(op, transform(), bounds, /*aa=*/true, /*fillsBounds=*/false);
+}
+
+CanvasStateHelper::ConservativeClip& CanvasStateHelper::clip() {
+ return writableEntry(&mClipStack);
+}
+
+SkMatrix& CanvasStateHelper::transform() {
+ return writableEntry(&mTransformStack);
}
bool CanvasStateHelper::internalRestore() {
@@ -80,45 +97,47 @@ bool CanvasStateHelper::internalRestore() {
mSaveStack.pop_back();
bool needsRestorePropagation = entry.layer;
if (entry.matrix) {
- mTransformStack.pop_back();
- mCurrentTransformIndex -= 1;
+ popEntry(&mTransformStack);
}
if (entry.clip) {
- // We need to push before accessing clip() to ensure the reference doesn't move
- // across vector resizes
- mClipStack.pop_back();
- mCurrentClipIndex -= 1;
+ popEntry(&mClipStack);
needsRestorePropagation = true;
}
return needsRestorePropagation;
}
SkRect CanvasStateHelper::getClipBounds() const {
- SkIRect ibounds = clip().getBounds();
-
- if (ibounds.isEmpty()) {
- return SkRect::MakeEmpty();
- }
+ SkIRect bounds = clip().bounds;
SkMatrix inverse;
// if we can't invert the CTM, we can't return local clip bounds
- if (!transform().invert(&inverse)) {
+ if (bounds.isEmpty() || !transform().invert(&inverse)) {
return SkRect::MakeEmpty();
}
- SkRect ret = SkRect::MakeEmpty();
- inverse.mapRect(&ret, SkRect::Make(ibounds));
- return ret;
+ return inverse.mapRect(SkRect::Make(bounds));
+}
+
+bool CanvasStateHelper::ConservativeClip::quickReject(const SkMatrix& matrix,
+ const SkRect& bounds) const {
+ SkRect devRect = matrix.mapRect(bounds);
+ return devRect.isFinite() &&
+ SkIRect::Intersects(this->bounds, aa ? devRect.roundOut() : devRect.round());
}
bool CanvasStateHelper::quickRejectRect(float left, float top, float right, float bottom) const {
- // TODO: Implement
- return false;
+ return clip().quickReject(transform(), SkRect::MakeLTRB(left, top, right, bottom));
}
bool CanvasStateHelper::quickRejectPath(const SkPath& path) const {
- // TODO: Implement
- return false;
+ if (this->isClipEmpty()) {
+ // reject everything (prioritized above path inverse fill type).
+ return true;
+ } else {
+ // Don't reject inverse-filled paths, since even if they are "empty" of points/verbs,
+ // they fill out the entire clip.
+ return !path.isInverseFillType() && clip().quickReject(transform(), path.getBounds());
+ }
}
} // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h
index f9a610129d3a..9f22b900e4ab 100644
--- a/libs/hwui/canvas/CanvasFrontend.h
+++ b/libs/hwui/canvas/CanvasFrontend.h
@@ -21,7 +21,6 @@
#include "CanvasOpBuffer.h"
#include <SaveFlags.h>
-#include <SkRasterClip.h>
#include <ui/FatVector.h>
#include <optional>
@@ -40,6 +39,26 @@ protected:
bool layer : 1 = false;
};
+ template <typename T>
+ struct DeferredEntry {
+ T entry;
+ int deferredSaveCount = 0;
+
+ DeferredEntry() = default;
+ DeferredEntry(const T& t) : entry(t) {}
+ };
+
+ struct ConservativeClip {
+ SkIRect bounds = SkIRect::MakeEmpty();
+ bool rect = true;
+ bool aa = false;
+
+ bool quickReject(const SkMatrix& matrix, const SkRect& bounds) const;
+
+ void apply(SkClipOp op, const SkMatrix& matrix, const SkRect& bounds, bool aa,
+ bool fillsBounds);
+ };
+
constexpr SaveEntry saveEntryForLayer() {
return {
.clip = true,
@@ -72,23 +91,47 @@ protected:
void internalClipRect(const SkRect& rect, SkClipOp op);
void internalClipPath(const SkPath& path, SkClipOp op);
+ // The canvas' clip will never expand beyond these bounds since intersect
+ // and difference operations only subtract pixels.
SkIRect mInitialBounds;
+ // Every save() gets a SaveEntry to track what needs to be restored.
FatVector<SaveEntry, 6> mSaveStack;
- FatVector<SkMatrix, 6> mTransformStack;
- FatVector<SkConservativeClip, 6> mClipStack;
+ // Transform and clip entries record a deferred save count and do not
+ // make a new entry until that particular state is modified.
+ FatVector<DeferredEntry<SkMatrix>, 6> mTransformStack;
+ FatVector<DeferredEntry<ConservativeClip>, 6> mClipStack;
- size_t mCurrentTransformIndex;
- size_t mCurrentClipIndex;
+ const ConservativeClip& clip() const { return mClipStack.back().entry; }
- const SkConservativeClip& clip() const {
- return mClipStack[mCurrentClipIndex];
+ ConservativeClip& clip();
+
+ void resetState(int width, int height);
+
+ // Stack manipulation for transform and clip stacks
+ template <typename T, size_t N>
+ void pushEntry(FatVector<DeferredEntry<T>, N>* stack) {
+ stack->back().deferredSaveCount += 1;
}
- SkConservativeClip& clip() {
- return mClipStack[mCurrentClipIndex];
+ template <typename T, size_t N>
+ void popEntry(FatVector<DeferredEntry<T>, N>* stack) {
+ if (!(stack->back().deferredSaveCount--)) {
+ stack->pop_back();
+ }
}
- void resetState(int width, int height);
+ template <typename T, size_t N>
+ T& writableEntry(FatVector<DeferredEntry<T>, N>* stack) {
+ DeferredEntry<T>& back = stack->back();
+ if (back.deferredSaveCount == 0) {
+ return back.entry;
+ } else {
+ back.deferredSaveCount -= 1;
+ // saved in case references move when re-allocating vector storage
+ T state = back.entry;
+ return stack->emplace_back(state).entry;
+ }
+ }
public:
int saveCount() const { return mSaveStack.size(); }
@@ -97,13 +140,14 @@ public:
bool quickRejectRect(float left, float top, float right, float bottom) const;
bool quickRejectPath(const SkPath& path) const;
- const SkMatrix& transform() const {
- return mTransformStack[mCurrentTransformIndex];
- }
+ bool isClipAA() const { return clip().aa; }
+ bool isClipEmpty() const { return clip().bounds.isEmpty(); }
+ bool isClipRect() const { return clip().rect; }
+ bool isClipComplex() const { return !isClipEmpty() && (isClipAA() || !isClipRect()); }
- SkMatrix& transform() {
- return mTransformStack[mCurrentTransformIndex];
- }
+ const SkMatrix& transform() const { return mTransformStack.back().entry; }
+
+ SkMatrix& transform();
// For compat with existing HWUI Canvas interface
void getMatrix(SkMatrix* outMatrix) const {
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index 17cd3ceb577c..8cb451528396 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -181,7 +181,7 @@ static const SkString stretchShader = SkString(R"(
);
coord.x = outU;
coord.y = outV;
- return sample(uContentTexture, coord);
+ return uContentTexture.eval(coord);
})");
static const float ZERO = 0.f;
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 876f5c895c60..d08bc5c583c2 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -157,7 +157,6 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
lazyPaint.emplace();
lazyPaint->setAlpha(mProperties.mAlpha);
lazyPaint->setColorFilter(mProperties.mColorFilter);
- lazyPaint->setFilterQuality(kLow_SkFilterQuality);
}
canvas->concat(matrix);
diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp
index 27a038d4598e..270d24af99fd 100644
--- a/libs/hwui/hwui/BlurDrawLooper.cpp
+++ b/libs/hwui/hwui/BlurDrawLooper.cpp
@@ -24,7 +24,7 @@ BlurDrawLooper::BlurDrawLooper(SkColor4f color, float blurSigma, SkPoint offset)
BlurDrawLooper::~BlurDrawLooper() = default;
-SkPoint BlurDrawLooper::apply(SkPaint* paint) const {
+SkPoint BlurDrawLooper::apply(Paint* paint) const {
paint->setColor(mColor);
if (mBlurSigma > 0) {
paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, mBlurSigma, true));
diff --git a/libs/hwui/hwui/BlurDrawLooper.h b/libs/hwui/hwui/BlurDrawLooper.h
index 7e6786f7dfbc..09a4e0f849b0 100644
--- a/libs/hwui/hwui/BlurDrawLooper.h
+++ b/libs/hwui/hwui/BlurDrawLooper.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
#define ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
-#include <SkPaint.h>
+#include <hwui/Paint.h>
#include <SkRefCnt.h>
class SkColorSpace;
@@ -30,10 +30,10 @@ public:
~BlurDrawLooper() override;
- // proc(SkPoint offset, const SkPaint& modifiedPaint)
+ // proc(SkPoint offset, const Paint& modifiedPaint)
template <typename DrawProc>
- void apply(const SkPaint& paint, DrawProc proc) const {
- SkPaint p(paint);
+ void apply(const Paint& paint, DrawProc proc) const {
+ Paint p(paint);
proc(this->apply(&p), p); // draw the shadow
proc({0, 0}, paint); // draw the original (on top)
}
@@ -43,7 +43,7 @@ private:
const float mBlurSigma;
const SkPoint mOffset;
- SkPoint apply(SkPaint* paint) const;
+ SkPoint apply(Paint* paint) const;
BlurDrawLooper(SkColor4f, float, SkPoint);
};
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 9023613478fc..82777646f3a2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -162,7 +162,7 @@ public:
virtual int save(SaveFlags::Flags flags) = 0;
virtual void restore() = 0;
virtual void restoreToCount(int saveCount) = 0;
- virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) = 0;
+ virtual void restoreUnclippedLayer(int saveCount, const Paint& paint) = 0;
virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint) = 0;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha) = 0;
@@ -185,6 +185,12 @@ public:
virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) = 0;
virtual bool clipPath(const SkPath* path, SkClipOp op) = 0;
+ // Resets clip to wide open, used to emulate the now-removed SkClipOp::kReplace on
+ // apps with compatibility < P. Canvases for version P and later are restricted to
+ // intersect and difference at the Java level, matching SkClipOp's definition.
+ // NOTE: These functions are deprecated and will be removed in a future release
+ virtual bool replaceClipRect_deprecated(float left, float top, float right, float bottom) = 0;
+ virtual bool replaceClipPath_deprecated(const SkPath* path) = 0;
// filters
virtual PaintFilter* getPaintFilter() = 0;
@@ -197,7 +203,7 @@ public:
// Canvas draw operations
// ----------------------------------------------------------------------------
virtual void drawColor(int color, SkBlendMode mode) = 0;
- virtual void drawPaint(const SkPaint& paint) = 0;
+ virtual void drawPaint(const Paint& paint) = 0;
// Geometry
virtual void drawPoint(float x, float y, const Paint& paint) = 0;
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 5d9fad5b676e..dd68f825b61d 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -24,7 +24,6 @@
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkEncodedOrigin.h>
-#include <SkFilterQuality.h>
#include <SkPaint.h>
#undef LOG_TAG
@@ -160,6 +159,8 @@ bool ImageDecoder::setOutColorType(SkColorType colorType) {
break;
case kRGBA_F16_SkColorType:
break;
+ case kRGBA_1010102_SkColorType:
+ break;
default:
return false;
}
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index b8029087cb4f..e359145feef7 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -95,6 +95,16 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
endHyphen, advances);
}
+minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
+ const Typeface* typeface, const uint16_t* buf,
+ size_t start, size_t count, size_t bufSize) {
+ minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::Range range(start, start + count);
+
+ return minikin::getFontExtent(textBuf, range, bidiFlags, minikinPaint);
+}
+
bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
return resolvedFace->fFontCollection->hasVariationSelector(codepoint, vs);
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index a15803ad2dca..009b84b140ea 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -56,6 +56,10 @@ public:
size_t start, size_t count, size_t bufSize,
float* advances);
+ static minikin::MinikinExtent getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
+ const Typeface* typeface, const uint16_t* buf,
+ size_t start, size_t count, size_t bufSize);
+
static bool hasVariationSelector(const Typeface* typeface, uint32_t codepoint,
uint32_t vs);
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index d9c9eeed03e9..4a8f3e10fc26 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -17,13 +17,13 @@
#ifndef ANDROID_GRAPHICS_PAINT_H_
#define ANDROID_GRAPHICS_PAINT_H_
-#include "BlurDrawLooper.h"
#include "Typeface.h"
#include <cutils/compiler.h>
#include <SkFont.h>
#include <SkPaint.h>
+#include <SkSamplingOptions.h>
#include <string>
#include <minikin/FontFamily.h>
@@ -32,6 +32,8 @@
namespace android {
+class BlurDrawLooper;
+
class Paint : public SkPaint {
public:
// Default values for underlined and strikethrough text,
@@ -60,7 +62,7 @@ public:
const SkFont& getSkFont() const { return mFont; }
BlurDrawLooper* getLooper() const { return mLooper.get(); }
- void setLooper(sk_sp<BlurDrawLooper> looper) { mLooper = std::move(looper); }
+ void setLooper(sk_sp<BlurDrawLooper> looper);
// These shadow the methods on SkPaint, but we need to so we can keep related
// attributes in-sync.
@@ -138,7 +140,15 @@ public:
void setDevKern(bool d) { mDevKern = d; }
// Deprecated -- bitmapshaders will be taking this flag explicitly
- bool isFilterBitmap() const { return this->getFilterQuality() != kNone_SkFilterQuality; }
+ bool isFilterBitmap() const { return mFilterBitmap; }
+ void setFilterBitmap(bool filter) { mFilterBitmap = filter; }
+
+ SkFilterMode filterMode() const {
+ return mFilterBitmap ? SkFilterMode::kLinear : SkFilterMode::kNearest;
+ }
+ SkSamplingOptions sampling() const {
+ return SkSamplingOptions(this->filterMode());
+ }
// The Java flags (Paint.java) no longer fit into the native apis directly.
// These methods handle converting to and from them and the native representations
@@ -169,6 +179,7 @@ private:
// nullptr is valid: it means the default typeface.
const Typeface* mTypeface = nullptr;
Align mAlign = kLeft_Align;
+ bool mFilterBitmap = false;
bool mStrikeThru = false;
bool mUnderline = false;
bool mDevKern = false;
diff --git a/libs/hwui/hwui/PaintFilter.h b/libs/hwui/hwui/PaintFilter.h
index 0e7b61977000..4996aa445316 100644
--- a/libs/hwui/hwui/PaintFilter.h
+++ b/libs/hwui/hwui/PaintFilter.h
@@ -1,17 +1,18 @@
#ifndef ANDROID_GRAPHICS_PAINT_FILTER_H_
#define ANDROID_GRAPHICS_PAINT_FILTER_H_
-class SkPaint;
+#include <SkRefCnt.h>
namespace android {
+class Paint;
+
class PaintFilter : public SkRefCnt {
public:
/**
* Called with the paint that will be used to draw.
* The implementation may modify the paint as they wish.
*/
- virtual void filter(SkPaint*) = 0;
virtual void filterFullPaint(Paint*) = 0;
};
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index fa2674fc2f5e..aac928f85924 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -15,6 +15,7 @@
*/
#include "Paint.h"
+#include "BlurDrawLooper.h"
namespace android {
@@ -43,6 +44,7 @@ Paint::Paint(const Paint& paint)
, mHyphenEdit(paint.mHyphenEdit)
, mTypeface(paint.mTypeface)
, mAlign(paint.mAlign)
+ , mFilterBitmap(paint.mFilterBitmap)
, mStrikeThru(paint.mStrikeThru)
, mUnderline(paint.mUnderline)
, mDevKern(paint.mDevKern) {}
@@ -62,6 +64,7 @@ Paint& Paint::operator=(const Paint& other) {
mHyphenEdit = other.mHyphenEdit;
mTypeface = other.mTypeface;
mAlign = other.mAlign;
+ mFilterBitmap = other.mFilterBitmap;
mStrikeThru = other.mStrikeThru;
mUnderline = other.mUnderline;
mDevKern = other.mDevKern;
@@ -77,6 +80,7 @@ bool operator==(const Paint& a, const Paint& b) {
a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
a.mTypeface == b.mTypeface && a.mAlign == b.mAlign &&
+ a.mFilterBitmap == b.mFilterBitmap &&
a.mStrikeThru == b.mStrikeThru && a.mUnderline == b.mUnderline &&
a.mDevKern == b.mDevKern;
}
@@ -88,11 +92,16 @@ void Paint::reset() {
mFont.setEdging(SkFont::Edging::kAlias);
mLooper.reset();
+ mFilterBitmap = false;
mStrikeThru = false;
mUnderline = false;
mDevKern = false;
}
+void Paint::setLooper(sk_sp<BlurDrawLooper> looper) {
+ mLooper = std::move(looper);
+}
+
void Paint::setAntiAlias(bool aa) {
// Java does not support/understand subpixel(lcd) antialiasing
SkASSERT(mFont.getEdging() != SkFont::Edging::kSubpixelAntiAlias);
@@ -131,9 +140,6 @@ static uint32_t paintToLegacyFlags(const SkPaint& paint) {
uint32_t flags = 0;
flags |= -(int)paint.isAntiAlias() & sAntiAliasFlag;
flags |= -(int)paint.isDither() & sDitherFlag;
- if (paint.getFilterQuality() != kNone_SkFilterQuality) {
- flags |= sFilterBitmapFlag;
- }
return flags;
}
@@ -150,12 +156,6 @@ static uint32_t fontToLegacyFlags(const SkFont& font) {
static void applyLegacyFlagsToPaint(uint32_t flags, SkPaint* paint) {
paint->setAntiAlias((flags & sAntiAliasFlag) != 0);
paint->setDither ((flags & sDitherFlag) != 0);
-
- if (flags & sFilterBitmapFlag) {
- paint->setFilterQuality(kLow_SkFilterQuality);
- } else {
- paint->setFilterQuality(kNone_SkFilterQuality);
- }
}
static void applyLegacyFlagsToFont(uint32_t flags, SkFont* font) {
@@ -182,18 +182,20 @@ void Paint::SetSkPaintJavaFlags(SkPaint* paint, uint32_t flags) {
uint32_t Paint::getJavaFlags() const {
uint32_t flags = paintToLegacyFlags(*this) | fontToLegacyFlags(mFont);
- flags |= -(int)mStrikeThru & sStrikeThruFlag;
- flags |= -(int)mUnderline & sUnderlineFlag;
- flags |= -(int)mDevKern & sDevKernFlag;
+ flags |= -(int)mStrikeThru & sStrikeThruFlag;
+ flags |= -(int)mUnderline & sUnderlineFlag;
+ flags |= -(int)mDevKern & sDevKernFlag;
+ flags |= -(int)mFilterBitmap & sFilterBitmapFlag;
return flags;
}
void Paint::setJavaFlags(uint32_t flags) {
applyLegacyFlagsToPaint(flags, this);
applyLegacyFlagsToFont(flags, &mFont);
- mStrikeThru = (flags & sStrikeThruFlag) != 0;
- mUnderline = (flags & sUnderlineFlag) != 0;
- mDevKern = (flags & sDevKernFlag) != 0;
+ mStrikeThru = (flags & sStrikeThruFlag) != 0;
+ mUnderline = (flags & sUnderlineFlag) != 0;
+ mDevKern = (flags & sDevKernFlag) != 0;
+ mFilterBitmap = (flags & sFilterBitmapFlag) != 0;
}
} // namespace android
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index c9433ec8a9da..c40b858268be 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -30,7 +30,8 @@
using namespace android;
-static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID;
+static jclass gAnimatedImageDrawableClass;
+static jmethodID gAnimatedImageDrawable_callOnAnimationEndMethodID;
// Note: jpostProcess holds a handle to the ImageDecoder.
static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
@@ -178,26 +179,23 @@ class InvokeListener : public MessageHandler {
public:
InvokeListener(JNIEnv* env, jobject javaObject) {
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
- // Hold a weak reference to break a cycle that would prevent GC.
- mWeakRef = env->NewWeakGlobalRef(javaObject);
+ mCallbackRef = env->NewGlobalRef(javaObject);
}
~InvokeListener() override {
auto* env = requireEnv(mJvm);
- env->DeleteWeakGlobalRef(mWeakRef);
+ env->DeleteGlobalRef(mCallbackRef);
}
virtual void handleMessage(const Message&) override {
auto* env = get_env_or_die(mJvm);
- jobject localRef = env->NewLocalRef(mWeakRef);
- if (localRef) {
- env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID);
- }
+ env->CallStaticVoidMethod(gAnimatedImageDrawableClass,
+ gAnimatedImageDrawable_callOnAnimationEndMethodID, mCallbackRef);
}
private:
JavaVM* mJvm;
- jweak mWeakRef;
+ jobject mCallbackRef;
};
class JniAnimationEndListener : public OnAnimationEndListener {
@@ -253,26 +251,31 @@ static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlo
}
static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
- { "nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",(void*) AnimatedImageDrawable_nCreate },
- { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer },
- { "nDraw", "(JJ)J", (void*) AnimatedImageDrawable_nDraw },
- { "nSetAlpha", "(JI)V", (void*) AnimatedImageDrawable_nSetAlpha },
- { "nGetAlpha", "(J)I", (void*) AnimatedImageDrawable_nGetAlpha },
- { "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter },
- { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning },
- { "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart },
- { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop },
- { "nGetRepeatCount", "(J)I", (void*) AnimatedImageDrawable_nGetRepeatCount },
- { "nSetRepeatCount", "(JI)V", (void*) AnimatedImageDrawable_nSetRepeatCount },
- { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
- { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
- { "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored },
- { "nSetBounds", "(JLandroid/graphics/Rect;)V", (void*) AnimatedImageDrawable_nSetBounds },
+ {"nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",
+ (void*)AnimatedImageDrawable_nCreate},
+ {"nGetNativeFinalizer", "()J", (void*)AnimatedImageDrawable_nGetNativeFinalizer},
+ {"nDraw", "(JJ)J", (void*)AnimatedImageDrawable_nDraw},
+ {"nSetAlpha", "(JI)V", (void*)AnimatedImageDrawable_nSetAlpha},
+ {"nGetAlpha", "(J)I", (void*)AnimatedImageDrawable_nGetAlpha},
+ {"nSetColorFilter", "(JJ)V", (void*)AnimatedImageDrawable_nSetColorFilter},
+ {"nIsRunning", "(J)Z", (void*)AnimatedImageDrawable_nIsRunning},
+ {"nStart", "(J)Z", (void*)AnimatedImageDrawable_nStart},
+ {"nStop", "(J)Z", (void*)AnimatedImageDrawable_nStop},
+ {"nGetRepeatCount", "(J)I", (void*)AnimatedImageDrawable_nGetRepeatCount},
+ {"nSetRepeatCount", "(JI)V", (void*)AnimatedImageDrawable_nSetRepeatCount},
+ {"nSetOnAnimationEndListener", "(JLjava/lang/ref/WeakReference;)V",
+ (void*)AnimatedImageDrawable_nSetOnAnimationEndListener},
+ {"nNativeByteSize", "(J)J", (void*)AnimatedImageDrawable_nNativeByteSize},
+ {"nSetMirrored", "(JZ)V", (void*)AnimatedImageDrawable_nSetMirrored},
+ {"nSetBounds", "(JLandroid/graphics/Rect;)V", (void*)AnimatedImageDrawable_nSetBounds},
};
int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
- jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable");
- gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V");
+ gAnimatedImageDrawableClass = reinterpret_cast<jclass>(env->NewGlobalRef(
+ FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable")));
+ gAnimatedImageDrawable_callOnAnimationEndMethodID =
+ GetStaticMethodIDOrDie(env, gAnimatedImageDrawableClass, "callOnAnimationEnd",
+ "(Ljava/lang/ref/WeakReference;)V");
return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 4003f0b65fb5..5db0783cf83e 100755
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -884,7 +884,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
jlong bitmapHandle, jint density, jobject parcel) {
#ifdef __ANDROID__ // Layoutlib does not support parcel
if (parcel == NULL) {
- SkDebugf("------- writeToParcel null parcel\n");
+ ALOGD("------- writeToParcel null parcel\n");
return JNI_FALSE;
}
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index 4cc05ef6f13b..1c20415dcc8f 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -137,9 +137,16 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in
auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle);
SkColorType decodeColorType = brd->computeOutputColorType(colorType);
- if (decodeColorType == kRGBA_F16_SkColorType && isHardware &&
+
+ if (isHardware) {
+ if (decodeColorType == kRGBA_F16_SkColorType &&
!uirenderer::HardwareBitmapUploader::hasFP16Support()) {
- decodeColorType = kN32_SkColorType;
+ decodeColorType = kN32_SkColorType;
+ }
+ if (decodeColorType == kRGBA_1010102_SkColorType &&
+ !uirenderer::HardwareBitmapUploader::has1010102Support()) {
+ decodeColorType = kN32_SkColorType;
+ }
}
// Set up the pixel allocator
diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
index 785a5dc995ab..15e529e169fc 100644
--- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
+++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
@@ -107,7 +107,7 @@ private:
jint n = env->CallIntMethod(fJavaInputStream,
gInputStream_readMethodID, fJavaByteArray, 0, requested);
if (checkException(env)) {
- SkDebugf("---- read threw an exception\n");
+ ALOGD("---- read threw an exception\n");
return bytesRead;
}
@@ -119,7 +119,7 @@ private:
env->GetByteArrayRegion(fJavaByteArray, 0, n,
reinterpret_cast<jbyte*>(buffer));
if (checkException(env)) {
- SkDebugf("---- read:GetByteArrayRegion threw an exception\n");
+ ALOGD("---- read:GetByteArrayRegion threw an exception\n");
return bytesRead;
}
@@ -136,7 +136,7 @@ private:
jlong skipped = env->CallLongMethod(fJavaInputStream,
gInputStream_skipMethodID, (jlong)size);
if (checkException(env)) {
- SkDebugf("------- skip threw an exception\n");
+ ALOGD("------- skip threw an exception\n");
return 0;
}
if (skipped < 0) {
@@ -236,7 +236,7 @@ public:
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
- SkDebugf("--- write:SetByteArrayElements threw an exception\n");
+ ALOGD("--- write:SetByteArrayElements threw an exception\n");
return false;
}
@@ -245,7 +245,7 @@ public:
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
- SkDebugf("------- write threw an exception\n");
+ ALOGD("------- write threw an exception\n");
return false;
}
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
index f84a4bd09073..fef51b8d2f79 100644
--- a/libs/hwui/jni/GIFMovie.cpp
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -10,11 +10,13 @@
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkStream.h"
-#include "SkTemplates.h"
-#include "SkUtils.h"
#include "gif_lib.h"
+#include <log/log.h>
+
+#include <string.h>
+
#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
#define DGifCloseFile(a, b) DGifCloseFile(a)
#endif
@@ -217,8 +219,9 @@ static void fillRect(SkBitmap* bm, GifWord left, GifWord top, GifWord width, Gif
copyHeight = bmHeight - top;
}
+ size_t bytes = copyWidth * SkColorTypeBytesPerPixel(bm->colorType());
for (; copyHeight > 0; copyHeight--) {
- sk_memset32(dst, col, copyWidth);
+ memset(dst, col, bytes);
dst += bmWidth;
}
}
@@ -244,7 +247,7 @@ static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObjec
}
if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
- SkDEBUGFAIL("bad colortable setup");
+ ALOGD("bad colortable setup");
return;
}
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 77f46beb2100..33669ac0a34e 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -365,6 +365,8 @@ jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) {
return kRGB_565_LegacyBitmapConfig;
case kAlpha_8_SkColorType:
return kA8_LegacyBitmapConfig;
+ case kRGBA_1010102_SkColorType:
+ return kRGBA_1010102_LegacyBitmapConfig;
case kUnknown_SkColorType:
default:
break;
@@ -374,14 +376,10 @@ jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) {
SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) {
const uint8_t gConfig2ColorType[] = {
- kUnknown_SkColorType,
- kAlpha_8_SkColorType,
- kUnknown_SkColorType, // Previously kIndex_8_SkColorType,
- kRGB_565_SkColorType,
- kARGB_4444_SkColorType,
- kN32_SkColorType,
- kRGBA_F16_SkColorType,
- kN32_SkColorType
+ kUnknown_SkColorType, kAlpha_8_SkColorType,
+ kUnknown_SkColorType, // Previously kIndex_8_SkColorType,
+ kRGB_565_SkColorType, kARGB_4444_SkColorType, kN32_SkColorType,
+ kRGBA_F16_SkColorType, kN32_SkColorType, kRGBA_1010102_SkColorType,
};
if (legacyConfig < 0 || legacyConfig > kLastEnum_LegacyBitmapConfig) {
@@ -399,15 +397,12 @@ AndroidBitmapFormat GraphicsJNI::getFormatFromConfig(JNIEnv* env, jobject jconfi
jint javaConfigId = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
const AndroidBitmapFormat config2BitmapFormat[] = {
- ANDROID_BITMAP_FORMAT_NONE,
- ANDROID_BITMAP_FORMAT_A_8,
- ANDROID_BITMAP_FORMAT_NONE, // Previously Config.Index_8
- ANDROID_BITMAP_FORMAT_RGB_565,
- ANDROID_BITMAP_FORMAT_RGBA_4444,
- ANDROID_BITMAP_FORMAT_RGBA_8888,
- ANDROID_BITMAP_FORMAT_RGBA_F16,
- ANDROID_BITMAP_FORMAT_NONE // Congfig.HARDWARE
- };
+ ANDROID_BITMAP_FORMAT_NONE, ANDROID_BITMAP_FORMAT_A_8,
+ ANDROID_BITMAP_FORMAT_NONE, // Previously Config.Index_8
+ ANDROID_BITMAP_FORMAT_RGB_565, ANDROID_BITMAP_FORMAT_RGBA_4444,
+ ANDROID_BITMAP_FORMAT_RGBA_8888, ANDROID_BITMAP_FORMAT_RGBA_F16,
+ ANDROID_BITMAP_FORMAT_NONE, // Congfig.HARDWARE
+ ANDROID_BITMAP_FORMAT_RGBA_1010102};
return config2BitmapFormat[javaConfigId];
}
@@ -430,6 +425,9 @@ jobject GraphicsJNI::getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat format
case ANDROID_BITMAP_FORMAT_RGBA_F16:
configId = kRGBA_16F_LegacyBitmapConfig;
break;
+ case ANDROID_BITMAP_FORMAT_RGBA_1010102:
+ configId = kRGBA_1010102_LegacyBitmapConfig;
+ break;
default:
break;
}
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index ba407f2164de..085a905abaf8 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -34,16 +34,17 @@ public:
// This enum must keep these int values, to match the int values
// in the java Bitmap.Config enum.
enum LegacyBitmapConfig {
- kNo_LegacyBitmapConfig = 0,
- kA8_LegacyBitmapConfig = 1,
- kIndex8_LegacyBitmapConfig = 2,
- kRGB_565_LegacyBitmapConfig = 3,
- kARGB_4444_LegacyBitmapConfig = 4,
- kARGB_8888_LegacyBitmapConfig = 5,
- kRGBA_16F_LegacyBitmapConfig = 6,
- kHardware_LegacyBitmapConfig = 7,
-
- kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig
+ kNo_LegacyBitmapConfig = 0,
+ kA8_LegacyBitmapConfig = 1,
+ kIndex8_LegacyBitmapConfig = 2,
+ kRGB_565_LegacyBitmapConfig = 3,
+ kARGB_4444_LegacyBitmapConfig = 4,
+ kARGB_8888_LegacyBitmapConfig = 5,
+ kRGBA_16F_LegacyBitmapConfig = 6,
+ kHardware_LegacyBitmapConfig = 7,
+ kRGBA_1010102_LegacyBitmapConfig = 8,
+
+ kLastEnum_LegacyBitmapConfig = kRGBA_1010102_LegacyBitmapConfig
};
static void setJavaVM(JavaVM* javaVM);
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp
index 3ca457f1a6a7..08fc80fbdafd 100644
--- a/libs/hwui/jni/NinePatch.cpp
+++ b/libs/hwui/jni/NinePatch.cpp
@@ -64,7 +64,7 @@ public:
}
static jlong validateNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) {
- size_t chunkSize = env->GetArrayLength(obj);
+ size_t chunkSize = obj != NULL ? env->GetArrayLength(obj) : 0;
if (chunkSize < (int) (sizeof(Res_png_9patch))) {
jniThrowRuntimeException(env, "Array too small for chunk.");
return 0;
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index bcec0fa8a1cc..f76863255153 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -541,26 +541,6 @@ namespace PaintGlue {
return result;
}
- // ------------------ @FastNative ---------------------------
-
- static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- ScopedUtfChars localesChars(env, locales);
- jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
- obj->setMinikinLocaleListId(minikinLocaleListId);
- return minikinLocaleListId;
- }
-
- static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- if (!settings) {
- paint->setFontFeatureSettings(std::string());
- } else {
- ScopedUtfChars settingsChars(env, settings);
- paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
- }
- }
-
static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) {
const int kElegantTop = 2500;
const int kElegantBottom = -1000;
@@ -593,6 +573,67 @@ namespace PaintGlue {
return spacing;
}
+ static void doFontExtent(JNIEnv* env, jlong paintHandle, const jchar buf[], jint start,
+ jint count, jint bufSize, jboolean isRtl, jobject fmi) {
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+ minikin::MinikinExtent extent =
+ MinikinUtils::getFontExtent(paint, bidiFlags, typeface, buf, start, count, bufSize);
+
+ SkFontMetrics metrics;
+ getMetricsInternal(paintHandle, &metrics);
+
+ metrics.fAscent = extent.ascent;
+ metrics.fDescent = extent.descent;
+
+ // If top/bottom is narrower than ascent/descent, adjust top/bottom to ascent/descent.
+ metrics.fTop = std::min(metrics.fAscent, metrics.fTop);
+ metrics.fBottom = std::max(metrics.fDescent, metrics.fBottom);
+
+ GraphicsJNI::set_metrics_int(env, fmi, metrics);
+ }
+
+ static void getFontMetricsIntForText___C(JNIEnv* env, jclass, jlong paintHandle,
+ jcharArray text, jint start, jint count, jint ctxStart,
+ jint ctxCount, jboolean isRtl, jobject fmi) {
+ ScopedCharArrayRO textArray(env, text);
+
+ doFontExtent(env, paintHandle, textArray.get() + ctxStart, start - ctxStart, count,
+ ctxCount, isRtl, fmi);
+ }
+
+ static void getFontMetricsIntForText___String(JNIEnv* env, jclass, jlong paintHandle,
+ jstring text, jint start, jint count,
+ jint ctxStart, jint ctxCount, jboolean isRtl,
+ jobject fmi) {
+ ScopedStringChars textChars(env, text);
+
+ doFontExtent(env, paintHandle, textChars.get() + ctxStart, start - ctxStart, count,
+ ctxCount, isRtl, fmi);
+ }
+
+ // ------------------ @FastNative ---------------------------
+
+ static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ ScopedUtfChars localesChars(env, locales);
+ jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
+ obj->setMinikinLocaleListId(minikinLocaleListId);
+ return minikinLocaleListId;
+ }
+
+ static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle,
+ jstring settings) {
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ if (!settings) {
+ paint->setFontFeatureSettings(std::string());
+ } else {
+ ScopedUtfChars settingsChars(env, settings);
+ paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+ }
+ }
+
static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
SkFontMetrics metrics;
SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
@@ -663,8 +704,7 @@ namespace PaintGlue {
}
static void setFilterBitmap(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean filterBitmap) {
- reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
- filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
+ reinterpret_cast<Paint*>(paintHandle)->setFilterBitmap(filterBitmap);
}
static void setDither(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean dither) {
@@ -1016,6 +1056,11 @@ static const JNINativeMethod methods[] = {
{"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 ----------------------
@@ -1094,6 +1139,7 @@ static const JNINativeMethod methods[] = {
{"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/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp
index 86d4742b949e..6b5310107c00 100644
--- a/libs/hwui/jni/PaintFilter.cpp
+++ b/libs/hwui/jni/PaintFilter.cpp
@@ -29,10 +29,6 @@ public:
fClearFlags = static_cast<uint16_t>(clearFlags);
fSetFlags = static_cast<uint16_t>(setFlags);
}
- void filter(SkPaint* paint) override {
- uint32_t flags = Paint::GetSkPaintJavaFlags(*paint);
- Paint::SetSkPaintJavaFlags(paint, (flags & ~fClearFlags) | fSetFlags);
- }
void filterFullPaint(Paint* paint) override {
paint->setJavaFlags((paint->getJavaFlags() & ~fClearFlags) | fSetFlags);
}
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index a48d7f734e29..213f35a81b88 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -127,6 +127,32 @@ static jlong createShaderEffect(
return reinterpret_cast<jlong>(shaderFilter.release());
}
+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 jlong createRuntimeShaderEffect(JNIEnv* env, jobject, jlong shaderBuilderHandle,
+ jstring inputShaderName) {
+ SkRuntimeShaderBuilder* builder =
+ reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilderHandle);
+ ScopedUtfChars name(env, inputShaderName);
+
+ if (builder->child(name.c_str()).fChild == nullptr) {
+ ThrowIAEFmt(env,
+ "unable to find a uniform with the name '%s' of the correct "
+ "type defined by the provided RuntimeShader",
+ name.c_str());
+ return 0;
+ }
+
+ sk_sp<SkImageFilter> filter = SkImageFilters::RuntimeShader(*builder, name.c_str(), nullptr);
+ return reinterpret_cast<jlong>(filter.release());
+}
+
static void RenderEffect_safeUnref(SkImageFilter* filter) {
SkSafeUnref(filter);
}
@@ -136,15 +162,16 @@ static jlong getRenderEffectFinalizer(JNIEnv*, jobject) {
}
static const JNINativeMethod gRenderEffectMethods[] = {
- {"nativeGetFinalizer", "()J", (void*)getRenderEffectFinalizer},
- {"nativeCreateOffsetEffect", "(FFJ)J", (void*)createOffsetEffect},
- {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect},
- {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
- {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
- {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
- {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
- {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect}
-};
+ {"nativeGetFinalizer", "()J", (void*)getRenderEffectFinalizer},
+ {"nativeCreateOffsetEffect", "(FFJ)J", (void*)createOffsetEffect},
+ {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect},
+ {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
+ {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
+ {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
+ {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
+ {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect},
+ {"nativeCreateRuntimeShaderEffect", "(JLjava/lang/String;)J",
+ (void*)createRuntimeShaderEffect}};
int register_android_graphics_RenderEffect(JNIEnv* env) {
android::RegisterMethodsOrDie(env, "android/graphics/RenderEffect",
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 90184432e8a4..899c7d4d75e2 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -64,7 +64,8 @@ static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) {
///////////////////////////////////////////////////////////////////////////////////////////////
static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
- jint tileModeX, jint tileModeY, bool filter) {
+ jint tileModeX, jint tileModeY, bool filter,
+ bool isDirectSampled) {
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
sk_sp<SkImage> image;
if (bitmapHandle) {
@@ -79,8 +80,12 @@ static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, j
}
SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest,
SkMipmapMode::kNone);
- sk_sp<SkShader> shader = image->makeShader(
- (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+ sk_sp<SkShader> shader;
+ if (isDirectSampled) {
+ shader = image->makeRawShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+ } else {
+ shader = image->makeShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+ }
ThrowIAE_IfNull(env, shader.get());
if (matrix) {
@@ -256,11 +261,10 @@ static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SkRuntimeShaderBuilder_delete));
}
-static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr,
- jboolean isOpaque) {
+static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr) {
SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
- sk_sp<SkShader> shader = builder->makeShader(matrix, isOpaque == JNI_TRUE);
+ sk_sp<SkShader> shader = builder->makeShader(matrix, false);
ThrowIAE_IfNull(env, shader);
return reinterpret_cast<jlong>(shader.release());
}
@@ -273,21 +277,99 @@ static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
return ret;
}
-static void RuntimeShader_updateUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
- jstring jUniformName, jfloatArray jvalues) {
+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 UpdateFloatUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder,
+ const char* uniformName, const float values[], int count,
+ bool isColor) {
+ SkRuntimeShaderBuilder::BuilderUniform 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 RuntimeShader_updateFloatUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jfloat value1, jfloat value2,
+ jfloat value3, jfloat value4, jint count) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ const float values[4] = {value1, value2, value3, value4};
+ UpdateFloatUniforms(env, builder, name.c_str(), values, count, false);
+}
+
+static void RuntimeShader_updateFloatArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jfloatArray jvalues,
+ jboolean isColor) {
SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
ScopedUtfChars name(env, jUniformName);
AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
+ UpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), isColor);
+}
- SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(name.c_str());
+static void UpdateIntUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder, const char* uniformName,
+ const int values[], int count) {
+ SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
if (uniform.fVar == nullptr) {
- ThrowIAEFmt(env, "unable to find uniform named %s", name.c_str());
- } else if (!uniform.set<float>(autoValues.ptr(), autoValues.length())) {
+ 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) * autoValues.length());
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
}
}
+static void RuntimeShader_updateIntUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jint value1, jint value2,
+ jint value3, jint value4, jint count) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ const int values[4] = {value1, value2, value3, value4};
+ UpdateIntUniforms(env, builder, name.c_str(), values, count);
+}
+
+static void RuntimeShader_updateIntArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jintArray jvalues) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ AutoJavaIntArray autoValues(env, jvalues, 0);
+ UpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length());
+}
+
static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder,
jstring jUniformName, jlong shaderHandle) {
SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
@@ -295,7 +377,7 @@ static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder
SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
SkRuntimeShaderBuilder::BuilderChild child = builder->child(name.c_str());
- if (child.fIndex == -1) {
+ if (child.fChild == nullptr) {
ThrowIAEFmt(env, "unable to find shader named %s", name.c_str());
return;
}
@@ -315,7 +397,7 @@ static const JNINativeMethod gShaderMethods[] = {
};
static const JNINativeMethod gBitmapShaderMethods[] = {
- { "nativeCreate", "(JJIIZ)J", (void*)BitmapShader_constructor },
+ {"nativeCreate", "(JJIIZZ)J", (void*)BitmapShader_constructor},
};
static const JNINativeMethod gLinearGradientMethods[] = {
@@ -336,9 +418,16 @@ static const JNINativeMethod gComposeShaderMethods[] = {
static const JNINativeMethod gRuntimeShaderMethods[] = {
{"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer},
- {"nativeCreateShader", "(JJZ)J", (void*)RuntimeShader_create},
+ {"nativeCreateShader", "(JJ)J", (void*)RuntimeShader_create},
{"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder},
- {"nativeUpdateUniforms", "(JLjava/lang/String;[F)V", (void*)RuntimeShader_updateUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
+ (void*)RuntimeShader_updateFloatArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
+ (void*)RuntimeShader_updateFloatUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
+ (void*)RuntimeShader_updateIntArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
+ (void*)RuntimeShader_updateIntUniforms},
{"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader},
};
diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp
index ac2f5b77d23a..106c6db57e18 100644
--- a/libs/hwui/jni/Utils.cpp
+++ b/libs/hwui/jni/Utils.cpp
@@ -18,6 +18,7 @@
#include "SkUtils.h"
#include "SkData.h"
+#include <inttypes.h>
#include <log/log.h>
using namespace android;
@@ -30,7 +31,7 @@ AssetStreamAdaptor::AssetStreamAdaptor(Asset* asset)
bool AssetStreamAdaptor::rewind() {
off64_t pos = fAsset->seek(0, SEEK_SET);
if (pos == (off64_t)-1) {
- SkDebugf("----- fAsset->seek(rewind) failed\n");
+ ALOGD("----- fAsset->seek(rewind) failed\n");
return false;
}
return true;
@@ -58,7 +59,7 @@ bool AssetStreamAdaptor::hasPosition() const {
size_t AssetStreamAdaptor::getPosition() const {
const off64_t offset = fAsset->seek(0, SEEK_CUR);
if (offset == -1) {
- SkDebugf("---- fAsset->seek(0, SEEK_CUR) failed\n");
+ ALOGD("---- fAsset->seek(0, SEEK_CUR) failed\n");
return 0;
}
@@ -67,7 +68,7 @@ size_t AssetStreamAdaptor::getPosition() const {
bool AssetStreamAdaptor::seek(size_t position) {
if (fAsset->seek(position, SEEK_SET) == -1) {
- SkDebugf("---- fAsset->seek(0, SEEK_SET) failed\n");
+ ALOGD("---- fAsset->seek(0, SEEK_SET) failed\n");
return false;
}
@@ -76,7 +77,7 @@ bool AssetStreamAdaptor::seek(size_t position) {
bool AssetStreamAdaptor::move(long offset) {
if (fAsset->seek(offset, SEEK_CUR) == -1) {
- SkDebugf("---- fAsset->seek(%i, SEEK_CUR) failed\n", offset);
+ ALOGD("---- fAsset->seek(%li, SEEK_CUR) failed\n", offset);
return false;
}
@@ -95,12 +96,12 @@ size_t AssetStreamAdaptor::read(void* buffer, size_t size) {
off64_t oldOffset = fAsset->seek(0, SEEK_CUR);
if (-1 == oldOffset) {
- SkDebugf("---- fAsset->seek(oldOffset) failed\n");
+ ALOGD("---- fAsset->seek(oldOffset) failed\n");
return 0;
}
off64_t newOffset = fAsset->seek(size, SEEK_CUR);
if (-1 == newOffset) {
- SkDebugf("---- fAsset->seek(%d) failed\n", size);
+ ALOGD("---- fAsset->seek(%zu) failed\n", size);
return 0;
}
amount = newOffset - oldOffset;
@@ -121,20 +122,20 @@ sk_sp<SkData> android::CopyAssetToData(Asset* asset) {
const off64_t seekReturnVal = asset->seek(0, SEEK_SET);
if ((off64_t)-1 == seekReturnVal) {
- SkDebugf("---- copyAsset: asset rewind failed\n");
+ ALOGD("---- copyAsset: asset rewind failed\n");
return NULL;
}
const off64_t size = asset->getLength();
if (size <= 0) {
- SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
+ ALOGD("---- copyAsset: asset->getLength() returned %" PRId64 "\n", size);
return NULL;
}
sk_sp<SkData> data(SkData::MakeUninitialized(size));
const off64_t len = asset->read(data->writable_data(), size);
if (len != size) {
- SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
+ ALOGD("---- copyAsset: asset->read(%" PRId64 ") returned %" PRId64 "\n", size, len);
return NULL;
}
@@ -143,7 +144,7 @@ sk_sp<SkData> android::CopyAssetToData(Asset* asset) {
jobject android::nullObjectReturn(const char msg[]) {
if (msg) {
- SkDebugf("--- %s\n", msg);
+ ALOGD("--- %s\n", msg);
}
return NULL;
}
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 689cf0bea741..77f42ae70268 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -85,7 +85,7 @@ Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) :
void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo,
uint8_t* yuv, int* offsets) {
- SkDebugf("onFlyCompress");
+ ALOGD("onFlyCompress");
JSAMPROW y[16];
JSAMPROW cb[8];
JSAMPROW cr[8];
@@ -161,7 +161,7 @@ Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) :
void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo,
uint8_t* yuv, int* offsets) {
- SkDebugf("onFlyCompress_422");
+ ALOGD("onFlyCompress_422");
JSAMPROW y[16];
JSAMPROW cb[16];
JSAMPROW cr[16];
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index a611f7ce2d14..0ef80ee10708 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -188,39 +188,57 @@ static jboolean quickRejectPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jl
return result ? JNI_TRUE : JNI_FALSE;
}
-// SkRegion::Op and SkClipOp are numerically identical, so we can freely cast
-// from one to the other (though SkClipOp is destined to become a strict subset)
+// SkClipOp is a strict subset of SkRegion::Op and is castable back and forth for their
+// shared operations (intersect and difference).
static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kDifference), "");
static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(SkClipOp::kIntersect), "");
-static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(SkClipOp::kUnion_deprecated), "");
-static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(SkClipOp::kXOR_deprecated), "");
-static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference_deprecated), "");
-static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace_deprecated), "");
-
-static SkClipOp opHandleToClipOp(jint opHandle) {
- // The opHandle is defined in Canvas.java to be Region::Op
- SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
-
- // In the future, when we no longer support the wide range of ops (e.g. Union, Xor)
- // this function can perform a range check and throw an unsupported-exception.
- // e.g. if (rgnOp != kIntersect && rgnOp != kDifference) throw...
-
- // Skia now takes a different type, SkClipOp, as the parameter to clipping calls
- // This type is binary compatible with SkRegion::Op, so a static_cast<> is safe.
- return static_cast<SkClipOp>(rgnOp);
-}
static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
jfloat r, jfloat b, jint opHandle) {
- bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b,
- opHandleToClipOp(opHandle));
+ // The opHandle is defined in Canvas.java to be Region::Op
+ SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
+ bool nonEmptyClip;
+ switch (rgnOp) {
+ case SkRegion::Op::kIntersect_Op:
+ case SkRegion::Op::kDifference_Op:
+ // Intersect and difference are supported clip operations
+ nonEmptyClip =
+ get_canvas(canvasHandle)->clipRect(l, t, r, b, static_cast<SkClipOp>(rgnOp));
+ break;
+ case SkRegion::Op::kReplace_Op:
+ // Replace is emulated to support legacy apps older than P
+ nonEmptyClip = get_canvas(canvasHandle)->replaceClipRect_deprecated(l, t, r, b);
+ break;
+ default:
+ // All other operations would expand the clip and are no longer supported,
+ // so log and skip (to avoid breaking legacy apps).
+ ALOGW("Ignoring unsupported clip operation %d", opHandle);
+ SkRect clipBounds; // ignored
+ nonEmptyClip = get_canvas(canvasHandle)->getClipBounds(&clipBounds);
+ break;
+ }
return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
}
static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle,
jint opHandle) {
+ SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
- bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, opHandleToClipOp(opHandle));
+ bool nonEmptyClip;
+ switch (rgnOp) {
+ case SkRegion::Op::kIntersect_Op:
+ case SkRegion::Op::kDifference_Op:
+ nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, static_cast<SkClipOp>(rgnOp));
+ break;
+ case SkRegion::Op::kReplace_Op:
+ nonEmptyClip = get_canvas(canvasHandle)->replaceClipPath_deprecated(path);
+ break;
+ default:
+ ALOGW("Ignoring unsupported clip operation %d", opHandle);
+ SkRect clipBounds; // ignored
+ nonEmptyClip = get_canvas(canvasHandle)->getClipBounds(&clipBounds);
+ break;
+ }
return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
}
@@ -233,7 +251,7 @@ static void drawColorLong(JNIEnv* env, jobject, jlong canvasHandle, jlong colorS
jlong colorLong, jint modeHandle) {
SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
- SkPaint p;
+ Paint p;
p.setColor4f(color, cs.get());
SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
@@ -421,7 +439,7 @@ static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmap
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawNinePatch(bitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale,
&filteredPaint);
@@ -443,7 +461,7 @@ static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHan
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawBitmap(bitmap, left, top, &filteredPaint);
} else {
canvas->drawBitmap(bitmap, left, top, paint);
@@ -458,7 +476,7 @@ static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHan
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
canvas->restore();
@@ -486,7 +504,7 @@ static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitma
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
} else {
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index b5536ad4830d..27865b3972d7 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -379,6 +379,13 @@ static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject c
proxy->dumpProfileInfo(fd, dumpFlags);
}
+static void android_view_ThreadedRenderer_dumpGlobalProfileInfo(JNIEnv* env, jobject clazz,
+ jobject javaFileDescriptor,
+ jint dumpFlags) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+ RenderProxy::dumpGraphicsMemory(fd, true, dumpFlags & DumpFlags::Reset);
+}
+
static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -426,28 +433,6 @@ private:
jobject mObject;
};
-class JWeakGlobalRefHolder {
-public:
- JWeakGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm) {
- mWeakRef = getenv(vm)->NewWeakGlobalRef(object);
- }
-
- virtual ~JWeakGlobalRefHolder() {
- if (mWeakRef != nullptr) getenv(mVm)->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
- }
-
- jobject ref() { return mWeakRef; }
- JavaVM* vm() { return mVm; }
-
-private:
- JWeakGlobalRefHolder(const JWeakGlobalRefHolder&) = delete;
- void operator=(const JWeakGlobalRefHolder&) = delete;
-
- JavaVM* mVm;
- jobject mWeakRef;
-};
-
using TextureMap = std::unordered_map<uint32_t, sk_sp<SkImage>>;
struct PictureCaptureState {
@@ -581,20 +566,16 @@ static void android_view_ThreadedRenderer_setASurfaceTransactionCallback(
} else {
JavaVM* vm = nullptr;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
- auto globalCallbackRef =
- std::make_shared<JWeakGlobalRefHolder>(vm, aSurfaceTransactionCallback);
+ auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(
+ vm, env->NewGlobalRef(aSurfaceTransactionCallback));
proxy->setASurfaceTransactionCallback(
[globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) -> bool {
JNIEnv* env = getenv(globalCallbackRef->vm());
- jobject localref = env->NewLocalRef(globalCallbackRef->ref());
- if (CC_UNLIKELY(!localref)) {
- return false;
- }
jboolean ret = env->CallBooleanMethod(
- localref, gASurfaceTransactionCallback.onMergeTransaction,
+ globalCallbackRef->object(),
+ gASurfaceTransactionCallback.onMergeTransaction,
static_cast<jlong>(transObj), static_cast<jlong>(scObj),
static_cast<jlong>(frameNr));
- env->DeleteLocalRef(localref);
return ret;
});
}
@@ -609,15 +590,11 @@ static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCall
JavaVM* vm = nullptr;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
auto globalCallbackRef =
- std::make_shared<JWeakGlobalRefHolder>(vm, callback);
+ std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() {
JNIEnv* env = getenv(globalCallbackRef->vm());
- jobject localref = env->NewLocalRef(globalCallbackRef->ref());
- if (CC_UNLIKELY(!localref)) {
- return;
- }
- env->CallVoidMethod(localref, gPrepareSurfaceControlForWebviewCallback.prepare);
- env->DeleteLocalRef(localref);
+ env->CallVoidMethod(globalCallbackRef->object(),
+ gPrepareSurfaceControlForWebviewCallback.prepare);
});
}
}
@@ -632,10 +609,19 @@ static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env,
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm,
env->NewGlobalRef(frameCallback));
- proxy->setFrameCallback([globalCallbackRef](int64_t frameNr) {
+ proxy->setFrameCallback([globalCallbackRef](int32_t syncResult,
+ int64_t frameNr) -> std::function<void(bool)> {
JNIEnv* env = getenv(globalCallbackRef->vm());
- env->CallVoidMethod(globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
- static_cast<jlong>(frameNr));
+ ScopedLocalRef<jobject> frameCommitCallback(
+ env, env->CallObjectMethod(
+ globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
+ static_cast<jint>(syncResult), static_cast<jlong>(frameNr)));
+ if (frameCommitCallback == nullptr) {
+ return nullptr;
+ }
+ sp<FrameCommitWrapper> wrapper =
+ sp<FrameCommitWrapper>::make(env, frameCommitCallback.get());
+ return [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); };
});
}
}
@@ -646,7 +632,7 @@ static void android_view_ThreadedRenderer_setFrameCommitCallback(JNIEnv* env, jo
if (!callback) {
proxy->setFrameCommitCallback(nullptr);
} else {
- sp<FrameCommitWrapper> wrapper = new FrameCommitWrapper{env, callback};
+ sp<FrameCommitWrapper> wrapper = sp<FrameCommitWrapper>::make(env, callback);
proxy->setFrameCommitCallback(
[wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); });
}
@@ -838,6 +824,14 @@ static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint
DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos);
}
+static void android_view_ThreadedRenderer_setDrawingEnabled(JNIEnv*, jclass, jboolean enabled) {
+ Properties::setDrawingEnabled(enabled);
+}
+
+static jboolean android_view_ThreadedRenderer_isDrawingEnabled(JNIEnv*, jclass) {
+ return Properties::isDrawingEnabled();
+}
+
// ----------------------------------------------------------------------------
// HardwareRendererObserver
// ----------------------------------------------------------------------------
@@ -932,6 +926,8 @@ static const JNINativeMethod gMethods[] = {
{"nNotifyFramePending", "(J)V", (void*)android_view_ThreadedRenderer_notifyFramePending},
{"nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V",
(void*)android_view_ThreadedRenderer_dumpProfileInfo},
+ {"nDumpGlobalProfileInfo", "(Ljava/io/FileDescriptor;I)V",
+ (void*)android_view_ThreadedRenderer_dumpGlobalProfileInfo},
{"setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V",
(void*)android_view_ThreadedRenderer_setupShadersDiskCache},
{"nAddRenderNode", "(JJZ)V", (void*)android_view_ThreadedRenderer_addRenderNode},
@@ -976,6 +972,8 @@ static const JNINativeMethod gMethods[] = {
{"preload", "()V", (void*)android_view_ThreadedRenderer_preload},
{"isWebViewOverlaysEnabled", "()Z",
(void*)android_view_ThreadedRenderer_isWebViewOverlaysEnabled},
+ {"nSetDrawingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDrawingEnabled},
+ {"nIsDrawingEnabled", "()Z", (void*)android_view_ThreadedRenderer_isDrawingEnabled},
};
static JavaVM* mJvm = nullptr;
@@ -1014,8 +1012,9 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) {
jclass frameCallbackClass = FindClassOrDie(env,
"android/graphics/HardwareRenderer$FrameDrawingCallback");
- gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
- "onFrameDraw", "(J)V");
+ gFrameDrawingCallback.onFrameDraw =
+ GetMethodIDOrDie(env, frameCallbackClass, "onFrameDraw",
+ "(IJ)Landroid/graphics/HardwareRenderer$FrameCommitCallback;");
jclass frameCommitClass =
FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameCommitCallback");
diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
index e5d5e75d0f3b..6cae5ffa397f 100644
--- a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
@@ -24,6 +24,7 @@
namespace android {
struct {
+ jclass clazz;
jmethodID callback;
} gHardwareRendererObserverClassInfo;
@@ -38,14 +39,13 @@ static JNIEnv* getenv(JavaVM* vm) {
HardwareRendererObserver::HardwareRendererObserver(JavaVM* vm, jobject observer,
bool waitForPresentTime)
: uirenderer::FrameMetricsObserver(waitForPresentTime), mVm(vm) {
- mObserverWeak = getenv(mVm)->NewWeakGlobalRef(observer);
- LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
- "unable to create frame stats observer reference");
+ mObserver = getenv(mVm)->NewGlobalRef(observer);
+ LOG_ALWAYS_FATAL_IF(mObserver == nullptr, "unable to create frame stats observer reference");
}
HardwareRendererObserver::~HardwareRendererObserver() {
JNIEnv* env = getenv(mVm);
- env->DeleteWeakGlobalRef(mObserverWeak);
+ env->DeleteGlobalRef(mObserver);
}
bool HardwareRendererObserver::getNextBuffer(JNIEnv* env, jlongArray metrics, int* dropCount) {
@@ -66,6 +66,8 @@ bool HardwareRendererObserver::getNextBuffer(JNIEnv* env, jlongArray metrics, in
}
void HardwareRendererObserver::notify(const int64_t* stats) {
+ if (!mKeepListening) return;
+
FrameMetricsNotification& elem = mRingBuffer[mNextFree];
if (!elem.hasData.load()) {
@@ -77,18 +79,17 @@ void HardwareRendererObserver::notify(const int64_t* stats) {
elem.hasData = true;
JNIEnv* env = getenv(mVm);
- jobject target = env->NewLocalRef(mObserverWeak);
- if (target != nullptr) {
- env->CallVoidMethod(target, gHardwareRendererObserverClassInfo.callback);
- env->DeleteLocalRef(target);
- }
+ mKeepListening = env->CallStaticBooleanMethod(gHardwareRendererObserverClassInfo.clazz,
+ gHardwareRendererObserverClassInfo.callback,
+ mObserver);
} else {
mDroppedReports++;
}
}
static jlong android_graphics_HardwareRendererObserver_createObserver(JNIEnv* env,
- jobject observerObj,
+ jobject /*clazz*/,
+ jobject weakRefThis,
jboolean waitForPresentTime) {
JavaVM* vm = nullptr;
if (env->GetJavaVM(&vm) != JNI_OK) {
@@ -97,7 +98,7 @@ static jlong android_graphics_HardwareRendererObserver_createObserver(JNIEnv* en
}
HardwareRendererObserver* observer =
- new HardwareRendererObserver(vm, observerObj, waitForPresentTime);
+ new HardwareRendererObserver(vm, weakRefThis, waitForPresentTime);
return reinterpret_cast<jlong>(observer);
}
@@ -114,7 +115,7 @@ static jint android_graphics_HardwareRendererObserver_getNextBuffer(JNIEnv* env,
}
static const std::array gMethods = {
- MAKE_JNI_NATIVE_METHOD("nCreateObserver", "(Z)J",
+ MAKE_JNI_NATIVE_METHOD("nCreateObserver", "(Ljava/lang/ref/WeakReference;Z)J",
android_graphics_HardwareRendererObserver_createObserver),
MAKE_JNI_NATIVE_METHOD("nGetNextBuffer", "(J[J)I",
android_graphics_HardwareRendererObserver_getNextBuffer),
@@ -123,8 +124,10 @@ static const std::array gMethods = {
int register_android_graphics_HardwareRendererObserver(JNIEnv* env) {
jclass observerClass = FindClassOrDie(env, "android/graphics/HardwareRendererObserver");
- gHardwareRendererObserverClassInfo.callback = GetMethodIDOrDie(env, observerClass,
- "notifyDataAvailable", "()V");
+ gHardwareRendererObserverClassInfo.clazz =
+ reinterpret_cast<jclass>(env->NewGlobalRef(observerClass));
+ gHardwareRendererObserverClassInfo.callback = GetStaticMethodIDOrDie(
+ env, observerClass, "invokeDataAvailable", "(Ljava/lang/ref/WeakReference;)Z");
return RegisterMethodsOrDie(env, "android/graphics/HardwareRendererObserver",
gMethods.data(), gMethods.size());
diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.h b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
index d3076140541b..5ee3e1669502 100644
--- a/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
+++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
@@ -63,7 +63,8 @@ private:
};
JavaVM* const mVm;
- jweak mObserverWeak;
+ jobject mObserver;
+ bool mKeepListening = true;
int mNextFree = 0;
int mNextInQueue = 0;
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index e1da1690518a..944393c63ad6 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -547,9 +547,12 @@ static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz,
// SurfaceView position callback
// ----------------------------------------------------------------------------
-jmethodID gPositionListener_PositionChangedMethod;
-jmethodID gPositionListener_ApplyStretchMethod;
-jmethodID gPositionListener_PositionLostMethod;
+struct {
+ jclass clazz;
+ jmethodID callPositionChanged;
+ jmethodID callApplyStretch;
+ jmethodID callPositionLost;
+} gPositionListener;
static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
jlong renderNodePtr, jobject listener) {
@@ -557,16 +560,16 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
public:
PositionListenerTrampoline(JNIEnv* env, jobject listener) {
env->GetJavaVM(&mVm);
- mWeakRef = env->NewWeakGlobalRef(listener);
+ mListener = env->NewGlobalRef(listener);
}
virtual ~PositionListenerTrampoline() {
- jnienv()->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
+ jnienv()->DeleteGlobalRef(mListener);
+ mListener = nullptr;
}
virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override {
- if (CC_UNLIKELY(!mWeakRef || !info.updateWindowPositions)) return;
+ if (CC_UNLIKELY(!mListener || !info.updateWindowPositions)) return;
Matrix4 transform;
info.damageAccumulator->computeCurrentTransform(&transform);
@@ -609,7 +612,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
}
virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
- if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return;
+ if (CC_UNLIKELY(!mListener || (info && !info->updateWindowPositions))) return;
if (mPreviousPosition.isEmpty()) {
return;
@@ -618,18 +621,16 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
ATRACE_NAME("SurfaceView position lost");
JNIEnv* env = jnienv();
- jobject localref = env->NewLocalRef(mWeakRef);
- if (CC_UNLIKELY(!localref)) {
- env->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
- return;
- }
#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
// TODO: Remember why this is synchronous and then make a comment
- env->CallVoidMethod(localref, gPositionListener_PositionLostMethod,
+ jboolean keepListening = env->CallStaticBooleanMethod(
+ gPositionListener.clazz, gPositionListener.callPositionLost, mListener,
info ? info->canvasContext.getFrameNumber() : 0);
+ if (!keepListening) {
+ env->DeleteGlobalRef(mListener);
+ mListener = nullptr;
+ }
#endif
- env->DeleteLocalRef(localref);
}
private:
@@ -684,28 +685,20 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
StretchEffectBehavior::Shader) {
JNIEnv* env = jnienv();
- jobject localref = env->NewLocalRef(mWeakRef);
- if (CC_UNLIKELY(!localref)) {
- env->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
- return;
- }
#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
SkVector stretchDirection = effect->getStretchDirection();
- env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
- info.canvasContext.getFrameNumber(),
- result.width,
- result.height,
- stretchDirection.fX,
- stretchDirection.fY,
- effect->maxStretchAmountX,
- effect->maxStretchAmountY,
- childRelativeBounds.left(),
- childRelativeBounds.top(),
- childRelativeBounds.right(),
- childRelativeBounds.bottom());
+ jboolean keepListening = env->CallStaticBooleanMethod(
+ gPositionListener.clazz, gPositionListener.callApplyStretch, mListener,
+ info.canvasContext.getFrameNumber(), result.width, result.height,
+ stretchDirection.fX, stretchDirection.fY, effect->maxStretchAmountX,
+ effect->maxStretchAmountY, childRelativeBounds.left(),
+ childRelativeBounds.top(), childRelativeBounds.right(),
+ childRelativeBounds.bottom());
+ if (!keepListening) {
+ env->DeleteGlobalRef(mListener);
+ mListener = nullptr;
+ }
#endif
- env->DeleteLocalRef(localref);
}
}
@@ -714,14 +707,12 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
ATRACE_NAME("Update SurfaceView position");
JNIEnv* env = jnienv();
- jobject localref = env->NewLocalRef(mWeakRef);
- if (CC_UNLIKELY(!localref)) {
- env->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
- } else {
- env->CallVoidMethod(localref, gPositionListener_PositionChangedMethod,
- frameNumber, left, top, right, bottom);
- env->DeleteLocalRef(localref);
+ jboolean keepListening = env->CallStaticBooleanMethod(
+ gPositionListener.clazz, gPositionListener.callPositionChanged, mListener,
+ frameNumber, left, top, right, bottom);
+ if (!keepListening) {
+ env->DeleteGlobalRef(mListener);
+ mListener = nullptr;
}
// We need to release ourselves here
@@ -729,7 +720,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
}
JavaVM* mVm;
- jobject mWeakRef;
+ jobject mListener;
uirenderer::Rect mPreviousPosition;
};
@@ -754,7 +745,7 @@ static const JNINativeMethod gMethods[] = {
{"nGetAllocatedSize", "(J)I", (void*)android_view_RenderNode_getAllocatedSize},
{"nAddAnimator", "(JJ)V", (void*)android_view_RenderNode_addAnimator},
{"nEndAllAnimators", "(J)V", (void*)android_view_RenderNode_endAllAnimators},
- {"nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V",
+ {"nRequestPositionUpdates", "(JLjava/lang/ref/WeakReference;)V",
(void*)android_view_RenderNode_requestPositionUpdates},
// ----------------------------------------------------------------------------
@@ -852,12 +843,13 @@ static const JNINativeMethod gMethods[] = {
int register_android_view_RenderNode(JNIEnv* env) {
jclass clazz = FindClassOrDie(env, "android/graphics/RenderNode$PositionUpdateListener");
- gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz,
- "positionChanged", "(JIIII)V");
- gPositionListener_ApplyStretchMethod =
- GetMethodIDOrDie(env, clazz, "applyStretch", "(JFFFFFFFFFF)V");
- gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz,
- "positionLost", "(J)V");
+ gPositionListener.clazz = MakeGlobalRefOrDie(env, clazz);
+ gPositionListener.callPositionChanged = GetStaticMethodIDOrDie(
+ env, clazz, "callPositionChanged", "(Ljava/lang/ref/WeakReference;JIIII)Z");
+ gPositionListener.callApplyStretch = GetStaticMethodIDOrDie(
+ env, clazz, "callApplyStretch", "(Ljava/lang/ref/WeakReference;JFFFFFFFFFF)Z");
+ gPositionListener.callPositionLost = GetStaticMethodIDOrDie(
+ env, clazz, "callPositionLost", "(Ljava/lang/ref/WeakReference;J)Z");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/libs/hwui/jni/text/MeasuredText.cpp b/libs/hwui/jni/text/MeasuredText.cpp
index 7793746ee285..76ea2d5c9ff3 100644
--- a/libs/hwui/jni/text/MeasuredText.cpp
+++ b/libs/hwui/jni/text/MeasuredText.cpp
@@ -65,11 +65,13 @@ static jlong nInitBuilder(CRITICAL_JNI_PARAMS) {
// Regular JNI
static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr,
- jlong paintPtr, jint start, jint end, jboolean isRtl) {
+ jlong paintPtr, jint lbStyle, jint lbWordStyle, jint start, jint end,
+ jboolean isRtl) {
Paint* paint = toPaint(paintPtr);
const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
- toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint), isRtl);
+ toBuilder(builderPtr)
+ ->addStyleRun(start, end, std::move(minikinPaint), lbStyle, lbWordStyle, isRtl);
}
// Regular JNI
@@ -80,15 +82,17 @@ static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong
}
// Regular JNI
-static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr,
- jlong hintPtr, jcharArray javaText, jboolean computeHyphenation,
- jboolean computeLayout) {
+static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr, jlong hintPtr,
+ jcharArray javaText, jboolean computeHyphenation,
+ jboolean computeLayout, jboolean fastHyphenationMode) {
ScopedCharArrayRO text(env, javaText);
const minikin::U16StringPiece textBuffer(text.get(), text.size());
// Pass the ownership to Java.
- return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation, computeLayout,
- toMeasuredParagraph(hintPtr)).release());
+ return toJLong(toBuilder(builderPtr)
+ ->build(textBuffer, computeHyphenation, computeLayout,
+ fastHyphenationMode, toMeasuredParagraph(hintPtr))
+ .release());
}
// Regular JNI
@@ -140,12 +144,12 @@ static jint nGetMemoryUsage(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
}
static const JNINativeMethod gMTBuilderMethods[] = {
- // MeasuredParagraphBuilder native functions.
- {"nInitBuilder", "()J", (void*) nInitBuilder},
- {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
- {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
- {"nBuildMeasuredText", "(JJ[CZZ)J", (void*) nBuildMeasuredText},
- {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
+ // MeasuredParagraphBuilder native functions.
+ {"nInitBuilder", "()J", (void*)nInitBuilder},
+ {"nAddStyleRun", "(JJIIIIZ)V", (void*)nAddStyleRun},
+ {"nAddReplacementRun", "(JJIIF)V", (void*)nAddReplacementRun},
+ {"nBuildMeasuredText", "(JJ[CZZZ)J", (void*)nBuildMeasuredText},
+ {"nFreeBuilder", "(J)V", (void*)nFreeBuilder},
};
static const JNINativeMethod gMTMethods[] = {
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index a6fb95832c03..8e4dd53069f4 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -160,7 +160,6 @@ static jlong TextShaper_Result_nReleaseFunc(CRITICAL_JNI_PARAMS) {
}
static const JNINativeMethod gMethods[] = {
- // Fast Natives
{"nativeShapeTextRun", "("
"[C" // text
"I" // start
diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt
index 77b8a44d85a1..087c006a8680 100644
--- a/libs/hwui/libhwui.map.txt
+++ b/libs/hwui/libhwui.map.txt
@@ -1,4 +1,4 @@
-LIBHWUI {
+LIBHWUI { # platform-only /* HWUI isn't current a module, so all of these are still platform-only */
global:
/* listing of all C APIs to be exposed by libhwui to consumers outside of the module */
ABitmap_getInfoFromJava;
@@ -39,7 +39,6 @@ LIBHWUI {
ARegionIterator_next;
ARegionIterator_getRect;
ARegionIterator_getTotalBounds;
- ARenderThread_dumpGraphicsMemory;
local:
*;
};
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 3580bed45a1f..3f89c0712407 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -52,6 +52,8 @@ protected:
mOutput << mIdent << "clipRegion" << std::endl;
}
+ void onResetClip() override { mOutput << mIdent << "resetClip" << std::endl; }
+
void onDrawPaint(const SkPaint&) override { mOutput << mIdent << "drawPaint" << std::endl; }
void onDrawPath(const SkPath&, const SkPaint&) override {
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 471a7f7af3b1..7c57bd52d6fe 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -15,12 +15,20 @@
*/
#include "LayerDrawable.h"
+
+#include <shaders/shaders.h>
+#include <utils/Color.h>
#include <utils/MathUtils.h>
+#include "DeviceInfo.h"
#include "GrBackendSurface.h"
#include "SkColorFilter.h"
+#include "SkRuntimeEffect.h"
#include "SkSurface.h"
#include "gl/GrGLTypes.h"
+#include "math/mat4.h"
+#include "system/graphics-base-v1.0.h"
+#include "system/window.h"
namespace android {
namespace uirenderer {
@@ -29,7 +37,8 @@ namespace skiapipeline {
void LayerDrawable::onDraw(SkCanvas* canvas) {
Layer* layer = mLayerUpdater->backingLayer();
if (layer) {
- DrawLayer(canvas->recordingContext(), canvas, layer, nullptr, nullptr, true);
+ SkRect srcRect = layer->getCurrentCropRect();
+ DrawLayer(canvas->recordingContext(), canvas, layer, &srcRect, nullptr, true);
}
}
@@ -67,6 +76,37 @@ 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(nullptr, false);
+}
+
+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;
+}
+
// TODO: Context arg probably doesn't belong here – do debug check at callsite instead.
bool LayerDrawable::DrawLayer(GrRecordingContext* context,
SkCanvas* canvas,
@@ -75,89 +115,108 @@ bool LayerDrawable::DrawLayer(GrRecordingContext* context,
const SkRect* dstRect,
bool useLayerTransform) {
if (context == nullptr) {
- SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
+ ALOGD("Attempting to draw LayerDrawable into an unsupported surface");
return false;
}
// transform the matrix based on the layer
- SkMatrix layerTransform = layer->getTransform();
+ // SkMatrix layerTransform = layer->getTransform();
+ const uint32_t windowTransform = layer->getWindowTransform();
sk_sp<SkImage> layerImage = layer->getImage();
const int layerWidth = layer->getWidth();
const int layerHeight = layer->getHeight();
if (layerImage) {
- SkMatrix textureMatrixInv;
- textureMatrixInv = layer->getTexTransform();
- // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
- // use bottom left origin and remove flipV and invert transformations.
- SkMatrix flipV;
- flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
- textureMatrixInv.preConcat(flipV);
- textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight);
- textureMatrixInv.postScale(layerImage->width(), layerImage->height());
- SkMatrix textureMatrix;
- if (!textureMatrixInv.invert(&textureMatrix)) {
- textureMatrix = textureMatrixInv;
- }
+ const int imageWidth = layerImage->width();
+ const int imageHeight = layerImage->height();
- SkMatrix matrix;
if (useLayerTransform) {
- matrix = SkMatrix::Concat(layerTransform, textureMatrix);
- } else {
- matrix = textureMatrix;
+ canvas->save();
+ canvas->concat(layer->getTransform());
}
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
paint.setColorFilter(layer->getColorFilter());
- const bool nonIdentityMatrix = !matrix.isIdentity();
- if (nonIdentityMatrix) {
- canvas->save();
- canvas->concat(matrix);
- }
const SkMatrix& totalMatrix = canvas->getTotalMatrix();
- if (dstRect || srcRect) {
- SkMatrix matrixInv;
- if (!matrix.invert(&matrixInv)) {
- matrixInv = matrix;
- }
- SkRect skiaSrcRect;
- if (srcRect) {
- skiaSrcRect = *srcRect;
- } else {
- skiaSrcRect = SkRect::MakeIWH(layerWidth, layerHeight);
- }
- matrixInv.mapRect(&skiaSrcRect);
- SkRect skiaDestRect;
- if (dstRect) {
- skiaDestRect = *dstRect;
- } else {
- skiaDestRect = SkRect::MakeIWH(layerWidth, layerHeight);
- }
- matrixInv.mapRect(&skiaDestRect);
- // If (matrix is a rect-to-rect transform)
- // and (src/dst buffers size match in screen coordinates)
- // and (src/dst corners align fractionally),
- // then use nearest neighbor, otherwise use bilerp sampling.
- // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
- // only for SrcOver blending and without color filter (readback uses Src blending).
- SkSamplingOptions sampling(SkFilterMode::kNearest);
- if (layer->getForceFilter() ||
- shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) {
- sampling = SkSamplingOptions(SkFilterMode::kLinear);
- }
- canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
- SkCanvas::kFast_SrcRectConstraint);
+ SkRect skiaSrcRect;
+ if (srcRect && !srcRect->isEmpty()) {
+ skiaSrcRect = *srcRect;
+ } else {
+ skiaSrcRect = SkRect::MakeIWH(imageWidth, imageHeight);
+ }
+ SkRect skiaDestRect;
+ if (dstRect && !dstRect->isEmpty()) {
+ skiaDestRect = (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
+ ? SkRect::MakeIWH(dstRect->height(), dstRect->width())
+ : SkRect::MakeIWH(dstRect->width(), dstRect->height());
+ } else {
+ skiaDestRect = (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
+ ? SkRect::MakeIWH(layerHeight, layerWidth)
+ : SkRect::MakeIWH(layerWidth, layerHeight);
+ }
+
+ const float px = skiaDestRect.centerX();
+ const float py = skiaDestRect.centerY();
+ SkMatrix m;
+ if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ m.postScale(-1.f, 1.f, px, py);
+ }
+ if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ m.postScale(1.f, -1.f, px, py);
+ }
+ if (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ m.postRotate(90, 0, 0);
+ m.postTranslate(skiaDestRect.height(), 0);
+ }
+ auto constraint = SkCanvas::kFast_SrcRectConstraint;
+ if (srcRect && !srcRect->isEmpty()) {
+ constraint = SkCanvas::kStrict_SrcRectConstraint;
+ }
+
+ canvas->save();
+ canvas->concat(m);
+
+ // If (matrix is a rect-to-rect transform)
+ // and (src/dst buffers size match in screen coordinates)
+ // and (src/dst corners align fractionally),
+ // then use nearest neighbor, otherwise use bilerp sampling.
+ // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
+ // only for SrcOver blending and without color filter (readback uses Src blending).
+ SkSamplingOptions sampling(SkFilterMode::kNearest);
+ if (layer->getForceFilter() || shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) {
+ 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 {
- SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height());
- SkSamplingOptions sampling(SkFilterMode::kNearest);
- if (layer->getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) {
- sampling = SkSamplingOptions(SkFilterMode::kLinear);
- }
- canvas->drawImage(layerImage.get(), 0, 0, sampling, &paint);
+ canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
+ constraint);
}
+
+ canvas->restore();
// restore the original matrix
- if (nonIdentityMatrix) {
+ if (useLayerTransform) {
canvas->restore();
}
}
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 48145d2331ee..507d3dcdcde9 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -88,6 +88,10 @@ static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect*
if (pendingClip) {
canvas->clipRect(*pendingClip);
}
+ const SkPath* path = outline.getPath();
+ if (path) {
+ canvas->clipPath(*path, SkClipOp::kIntersect, true);
+ }
return;
}
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 9bca4df577c9..744739accb2c 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -91,6 +91,8 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con
fboInfo.fFormat = GL_RGBA8;
} else if (colorType == kRGBA_1010102_SkColorType) {
fboInfo.fFormat = GL_RGB10_A2;
+ } else if (colorType == kAlpha_8_SkColorType) {
+ fboInfo.fFormat = GL_R8;
} else {
LOG_ALWAYS_FATAL("Unsupported color type.");
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 4e7471d5d888..bc386feb2d6f 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -613,6 +613,10 @@ void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) {
mSurfaceColorType = SkColorType::kRGBA_1010102_SkColorType;
mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020);
break;
+ case ColorMode::A8:
+ mSurfaceColorType = SkColorType::kAlpha_8_SkColorType;
+ mSurfaceColorSpace = nullptr;
+ break;
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 76c4a03d3a91..9c51e628e04a 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -187,28 +187,18 @@ void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) {
// kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
// older.
- if (sApiLevel <= 27 && paint.getBlendMode() == SkBlendMode::kClear) {
+ if (sApiLevel <= 27 && paint.asBlendMode() == SkBlendMode::kClear) {
paint.setBlendMode(SkBlendMode::kDstOut);
}
}
-static SkFilterMode Paint_to_filter(const SkPaint& paint) {
- return paint.getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear
- : SkFilterMode::kNearest;
-}
-
-static SkSamplingOptions Paint_to_sampling(const SkPaint& paint) {
- // Android only has 1-bit for "filter", so we don't try to cons-up mipmaps or cubics
- return SkSamplingOptions(Paint_to_filter(paint), SkMipmapMode::kNone);
-}
-
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
sk_sp<SkImage> image = bitmap.makeImage();
applyLooper(
paint,
- [&](const SkPaint& p) {
- mRecorder.drawImage(image, left, top, Paint_to_sampling(p), &p, bitmap.palette());
+ [&](const Paint& p) {
+ mRecorder.drawImage(image, left, top, p.sampling(), &p, bitmap.palette());
},
FilterForImage);
@@ -228,8 +218,8 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con
applyLooper(
paint,
- [&](const SkPaint& p) {
- mRecorder.drawImage(image, 0, 0, Paint_to_sampling(p), &p, bitmap.palette());
+ [&](const Paint& p) {
+ mRecorder.drawImage(image, 0, 0, p.sampling(), &p, bitmap.palette());
},
FilterForImage);
@@ -248,8 +238,8 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop
applyLooper(
paint,
- [&](const SkPaint& p) {
- mRecorder.drawImageRect(image, srcRect, dstRect, Paint_to_sampling(p), &p,
+ [&](const Paint& p) {
+ mRecorder.drawImageRect(image, srcRect, dstRect, p.sampling(), &p,
SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
},
FilterForImage);
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 8abf4534a04c..e6ef95b9cf91 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -72,6 +72,7 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
.clip_top = mClip.fTop,
.clip_right = mClip.fRight,
.clip_bottom = mClip.fBottom,
+ .is_layer = !vulkan_info.fFromSwapchainOrAndroidWindow,
};
mat4.getColMajor(&params.transform[0]);
params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer;
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index ddfb66f84f28..3c7617d35c7c 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -68,7 +68,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
ATRACE_CALL();
if (canvas->recordingContext() == nullptr) {
- SkDEBUGF(("Attempting to draw VkInteropFunctor into an unsupported surface"));
+ ALOGD("Attempting to draw VkInteropFunctor into an unsupported surface");
return;
}
diff --git a/libs/hwui/private/hwui/DrawVkInfo.h b/libs/hwui/private/hwui/DrawVkInfo.h
index 4ae0f5a0a2e5..5c596576df4e 100644
--- a/libs/hwui/private/hwui/DrawVkInfo.h
+++ b/libs/hwui/private/hwui/DrawVkInfo.h
@@ -68,6 +68,9 @@ struct VkFunctorDrawParams {
int clip_top;
int clip_right;
int clip_bottom;
+
+ // Input: Whether destination surface is offscreen surface.
+ bool is_layer;
};
} // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index a066e6f7c693..bdad7727071f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -18,15 +18,16 @@
#include <apex/window.h>
#include <fcntl.h>
+#include <gui/TraceUtils.h>
#include <strings.h>
#include <sys/stat.h>
+#include <ui/Fence.h>
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <functional>
-#include <gui/TraceUtils.h>
#include "../Properties.h"
#include "AnimationContext.h"
#include "Frame.h"
@@ -203,9 +204,10 @@ void CanvasContext::setSurfaceControl(ASurfaceControl* surfaceControl) {
mSurfaceControl = surfaceControl;
mSurfaceControlGenerationId++;
mExpectSurfaceStats = surfaceControl != nullptr;
- if (mSurfaceControl != nullptr) {
+ if (mExpectSurfaceStats) {
funcs.acquireFunc(mSurfaceControl);
- funcs.registerListenerFunc(surfaceControl, this, &onSurfaceStatsAvailable);
+ funcs.registerListenerFunc(surfaceControl, mSurfaceControlGenerationId, this,
+ &onSurfaceStatsAvailable);
}
}
@@ -218,7 +220,7 @@ void CanvasContext::setupPipelineSurface() {
}
- mFrameNumber = -1;
+ mFrameNumber = 0;
if (mNativeSurface != nullptr && hasSurface) {
mHaveNewSurface = true;
@@ -256,7 +258,7 @@ void CanvasContext::setStopped(bool stopped) {
}
void CanvasContext::allocateBuffers() {
- if (mNativeSurface) {
+ if (mNativeSurface && Properties::isDrawingEnabled()) {
ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow());
}
}
@@ -480,7 +482,8 @@ nsecs_t CanvasContext::draw() {
SkRect dirty;
mDamageAccumulator.finish(&dirty);
- if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
+ if (!Properties::isDrawingEnabled() ||
+ (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
if (auto grContext = getGrContext()) {
// Submit to ensure that any texture uploads complete and Skia can
@@ -507,11 +510,13 @@ nsecs_t CanvasContext::draw() {
Frame frame = mRenderPipeline->getFrame();
SkRect windowDirty = computeDirtyRect(frame, &dirty);
+ ATRACE_FORMAT("Drawing " RECT_STRING, SK_RECT_ARGS(dirty));
+
bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
&(profiler()));
- int64_t frameCompleteNr = getFrameNumber();
+ uint64_t frameCompleteNr = getFrameNumber();
waitOnFences();
@@ -521,8 +526,9 @@ nsecs_t CanvasContext::draw() {
if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
const auto inputEventId =
static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
- native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), vsyncId,
- inputEventId);
+ native_window_set_frame_timeline_info(
+ mNativeSurface->getNativeWindow(), vsyncId, inputEventId,
+ mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime));
}
}
@@ -579,7 +585,7 @@ nsecs_t CanvasContext::draw() {
mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration;
mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration;
mHaveNewSurface = false;
- mFrameNumber = -1;
+ mFrameNumber = 0;
} else {
mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0;
mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0;
@@ -612,16 +618,20 @@ nsecs_t CanvasContext::draw() {
if (requireSwap) {
if (mExpectSurfaceStats) {
reportMetricsWithPresentTime();
- std::lock_guard lock(mLast4FrameInfosMutex);
- std::pair<FrameInfo*, int64_t>& next = mLast4FrameInfos.next();
- next.first = mCurrentFrameInfo;
- next.second = frameCompleteNr;
+ { // acquire lock
+ std::lock_guard lock(mLast4FrameMetricsInfosMutex);
+ FrameMetricsInfo& next = mLast4FrameMetricsInfos.next();
+ next.frameInfo = mCurrentFrameInfo;
+ next.frameNumber = frameCompleteNr;
+ next.surfaceId = mSurfaceControlGenerationId;
+ } // release lock
} else {
mCurrentFrameInfo->markFrameCompleted();
mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted)
= mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted);
std::scoped_lock lock(mFrameMetricsReporterMutex);
- mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter);
+ mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter, frameCompleteNr,
+ mSurfaceControlGenerationId);
}
}
@@ -657,14 +667,18 @@ void CanvasContext::reportMetricsWithPresentTime() {
ATRACE_CALL();
FrameInfo* forthBehind;
int64_t frameNumber;
+ int32_t surfaceControlId;
+
{ // acquire lock
- std::scoped_lock lock(mLast4FrameInfosMutex);
- if (mLast4FrameInfos.size() != mLast4FrameInfos.capacity()) {
+ std::scoped_lock lock(mLast4FrameMetricsInfosMutex);
+ if (mLast4FrameMetricsInfos.size() != mLast4FrameMetricsInfos.capacity()) {
// Not enough frames yet
return;
}
- // Surface object keeps stats for the last 8 frames.
- std::tie(forthBehind, frameNumber) = mLast4FrameInfos.front();
+ auto frameMetricsInfo = mLast4FrameMetricsInfos.front();
+ forthBehind = frameMetricsInfo.frameInfo;
+ frameNumber = frameMetricsInfo.frameNumber;
+ surfaceControlId = frameMetricsInfo.surfaceId;
} // release lock
nsecs_t presentTime = 0;
@@ -679,40 +693,70 @@ void CanvasContext::reportMetricsWithPresentTime() {
{ // acquire lock
std::scoped_lock lock(mFrameMetricsReporterMutex);
if (mFrameMetricsReporter != nullptr) {
- mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/);
+ mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/,
+ frameNumber, surfaceControlId);
}
} // release lock
}
-FrameInfo* CanvasContext::getFrameInfoFromLast4(uint64_t frameNumber) {
- std::scoped_lock lock(mLast4FrameInfosMutex);
- for (size_t i = 0; i < mLast4FrameInfos.size(); i++) {
- if (mLast4FrameInfos[i].second == frameNumber) {
- return mLast4FrameInfos[i].first;
+void CanvasContext::addFrameMetricsObserver(FrameMetricsObserver* observer) {
+ std::scoped_lock lock(mFrameMetricsReporterMutex);
+ if (mFrameMetricsReporter.get() == nullptr) {
+ mFrameMetricsReporter.reset(new FrameMetricsReporter());
+ }
+
+ // We want to make sure we aren't reporting frames that have already been queued by the
+ // BufferQueueProducer on the rendner thread but are still pending the callback to report their
+ // their frame metrics.
+ uint64_t nextFrameNumber = getFrameNumber();
+ observer->reportMetricsFrom(nextFrameNumber, mSurfaceControlGenerationId);
+ mFrameMetricsReporter->addObserver(observer);
+}
+
+void CanvasContext::removeFrameMetricsObserver(FrameMetricsObserver* observer) {
+ std::scoped_lock lock(mFrameMetricsReporterMutex);
+ if (mFrameMetricsReporter.get() != nullptr) {
+ mFrameMetricsReporter->removeObserver(observer);
+ if (!mFrameMetricsReporter->hasObservers()) {
+ mFrameMetricsReporter.reset(nullptr);
}
}
- return nullptr;
}
-void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
- ASurfaceControlStats* stats) {
+FrameInfo* CanvasContext::getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId) {
+ std::scoped_lock lock(mLast4FrameMetricsInfosMutex);
+ for (size_t i = 0; i < mLast4FrameMetricsInfos.size(); i++) {
+ if (mLast4FrameMetricsInfos[i].frameNumber == frameNumber &&
+ mLast4FrameMetricsInfos[i].surfaceId == surfaceControlId) {
+ return mLast4FrameMetricsInfos[i].frameInfo;
+ }
+ }
+
+ return nullptr;
+}
- CanvasContext* instance = static_cast<CanvasContext*>(context);
+void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceControlId,
+ ASurfaceControlStats* stats) {
+ auto* instance = static_cast<CanvasContext*>(context);
const ASurfaceControlFunctions& functions =
instance->mRenderThread.getASurfaceControlFunctions();
nsecs_t gpuCompleteTime = functions.getAcquireTimeFunc(stats);
+ if (gpuCompleteTime == Fence::SIGNAL_TIME_PENDING) {
+ gpuCompleteTime = -1;
+ }
uint64_t frameNumber = functions.getFrameNumberFunc(stats);
- FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber);
+ FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber, surfaceControlId);
if (frameInfo != nullptr) {
frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime,
frameInfo->get(FrameInfoIndex::SwapBuffersCompleted));
frameInfo->set(FrameInfoIndex::GpuCompleted) = gpuCompleteTime;
std::scoped_lock lock(instance->mFrameMetricsReporterMutex);
- instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter);
+ instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter, frameNumber,
+ surfaceControlId);
}
}
@@ -853,9 +897,9 @@ void CanvasContext::enqueueFrameWork(std::function<void()>&& func) {
mFrameFences.push_back(CommonPool::async(std::move(func)));
}
-int64_t CanvasContext::getFrameNumber() {
- // mFrameNumber is reset to -1 when the surface changes or we swap buffers
- if (mFrameNumber == -1 && mNativeSurface.get()) {
+uint64_t CanvasContext::getFrameNumber() {
+ // mFrameNumber is reset to 0 when the surface changes or we swap buffers
+ if (mFrameNumber == 0 && mNativeSurface.get()) {
mFrameNumber = ANativeWindow_getNextFrameId(mNativeSurface->getNativeWindow());
}
return mFrameNumber;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 9df429badd5e..0caf76a26539 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -90,9 +90,17 @@ public:
* and false otherwise (e.g. cache limits have been exceeded).
*/
bool pinImages(std::vector<SkImage*>& mutableImages) {
+ if (!Properties::isDrawingEnabled()) {
+ return true;
+ }
return mRenderPipeline->pinImages(mutableImages);
}
- bool pinImages(LsaVector<sk_sp<Bitmap>>& images) { return mRenderPipeline->pinImages(images); }
+ bool pinImages(LsaVector<sk_sp<Bitmap>>& images) {
+ if (!Properties::isDrawingEnabled()) {
+ return true;
+ }
+ return mRenderPipeline->pinImages(images);
+ }
/**
* Unpin any image that had be previously pinned to the GPU cache
@@ -159,29 +167,13 @@ public:
void setContentDrawBounds(const Rect& bounds) { mContentDrawBounds = bounds; }
- void addFrameMetricsObserver(FrameMetricsObserver* observer) {
- std::scoped_lock lock(mFrameMetricsReporterMutex);
- if (mFrameMetricsReporter.get() == nullptr) {
- mFrameMetricsReporter.reset(new FrameMetricsReporter());
- }
-
- mFrameMetricsReporter->addObserver(observer);
- }
-
- void removeFrameMetricsObserver(FrameMetricsObserver* observer) {
- std::scoped_lock lock(mFrameMetricsReporterMutex);
- if (mFrameMetricsReporter.get() != nullptr) {
- mFrameMetricsReporter->removeObserver(observer);
- if (!mFrameMetricsReporter->hasObservers()) {
- mFrameMetricsReporter.reset(nullptr);
- }
- }
- }
+ void addFrameMetricsObserver(FrameMetricsObserver* observer);
+ void removeFrameMetricsObserver(FrameMetricsObserver* observer);
// Used to queue up work that needs to be completed before this frame completes
void enqueueFrameWork(std::function<void()>&& func);
- int64_t getFrameNumber();
+ uint64_t getFrameNumber();
void waitOnFences();
@@ -204,8 +196,8 @@ public:
SkISize getNextFrameSize() const;
// Called when SurfaceStats are available.
- static void onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
- ASurfaceControlStats* stats);
+ static void onSurfaceStatsAvailable(void* context, int32_t surfaceControlId,
+ ASurfaceControlStats* stats);
void setASurfaceTransactionCallback(
const std::function<bool(int64_t, int64_t, int64_t)>& callback) {
@@ -246,7 +238,13 @@ private:
*/
void reportMetricsWithPresentTime();
- FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber);
+ struct FrameMetricsInfo {
+ FrameInfo* frameInfo;
+ int64_t frameNumber;
+ int32_t surfaceId;
+ };
+
+ FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId);
// The same type as Frame.mWidth and Frame.mHeight
int32_t mLastFrameWidth = 0;
@@ -258,7 +256,10 @@ private:
// NULL to remove the reference
ASurfaceControl* mSurfaceControl = nullptr;
// id to track surface control changes and WebViewFunctor uses it to determine
- // whether reparenting is needed
+ // whether reparenting is needed also used by FrameMetricsReporter to determine
+ // if a frame is from an "old" surface (i.e. one that existed before the
+ // observer was attched) and therefore shouldn't be reported.
+ // NOTE: It is important that this is an increasing counter.
int32_t mSurfaceControlGenerationId = 0;
// stopped indicates the CanvasContext will reject actual redraw operations,
// and defer repaint until it is un-stopped
@@ -280,7 +281,8 @@ private:
// Need at least 4 because we do quad buffer. Add a 5th for good measure.
RingBuffer<SwapHistory, 5> mSwapHistory;
- int64_t mFrameNumber = -1;
+ // Frame numbers start at 1, 0 means uninitialized
+ uint64_t mFrameNumber = 0;
int64_t mDamageId = 0;
// last vsync for a dropped frame due to stuffed queue
@@ -300,10 +302,11 @@ private:
FrameInfo* mCurrentFrameInfo = nullptr;
- // List of frames that are awaiting GPU completion reporting
- RingBuffer<std::pair<FrameInfo*, int64_t>, 4> mLast4FrameInfos
- GUARDED_BY(mLast4FrameInfosMutex);
- std::mutex mLast4FrameInfosMutex;
+ // List of data of frames that are awaiting GPU completion reporting. Used to compute frame
+ // metrics and determine whether or not to report the metrics.
+ RingBuffer<FrameMetricsInfo, 4> mLast4FrameMetricsInfos
+ GUARDED_BY(mLast4FrameMetricsInfosMutex);
+ std::mutex mLast4FrameMetricsInfosMutex;
std::string mName;
JankTracker mJankTracker;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 94aedd0f43be..8c98c723dbb3 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -158,7 +158,8 @@ void DrawFrameTask::run() {
// Grab a copy of everything we need
CanvasContext* context = mContext;
- std::function<void(int64_t)> frameCallback = std::move(mFrameCallback);
+ std::function<std::function<void(bool)>(int32_t, int64_t)> frameCallback =
+ std::move(mFrameCallback);
std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
mFrameCallback = nullptr;
mFrameCompleteCallback = nullptr;
@@ -173,8 +174,13 @@ void DrawFrameTask::run() {
// Even if we aren't drawing this vsync pulse the next frame number will still be accurate
if (CC_UNLIKELY(frameCallback)) {
- context->enqueueFrameWork(
- [frameCallback, frameNr = context->getFrameNumber()]() { frameCallback(frameNr); });
+ context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
+ frameNr = context->getFrameNumber()]() {
+ auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
+ if (frameCommitCallback) {
+ context->addFrameCommitListener(std::move(frameCommitCallback));
+ }
+ });
}
nsecs_t dequeueBufferDuration = 0;
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index e3ea802b07b9..25ed935b7a76 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,19 +16,18 @@
#ifndef DRAWFRAMETASK_H
#define DRAWFRAMETASK_H
-#include <optional>
-#include <vector>
-
-#include <performance_hint_private.h>
+#include <android/performance_hint.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
-#include "RenderTask.h"
+#include <optional>
+#include <vector>
#include "../FrameInfo.h"
#include "../Rect.h"
#include "../TreeInfo.h"
+#include "RenderTask.h"
namespace android {
namespace uirenderer {
@@ -77,7 +76,7 @@ public:
void run();
- void setFrameCallback(std::function<void(int64_t)>&& callback) {
+ void setFrameCallback(std::function<std::function<void(bool)>(int32_t, int64_t)>&& callback) {
mFrameCallback = std::move(callback);
}
@@ -126,7 +125,7 @@ private:
int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
- std::function<void(int64_t)> mFrameCallback;
+ std::function<std::function<void(bool)>(int32_t, int64_t)> mFrameCallback;
std::function<void(bool)> mFrameCommitCallback;
std::function<void()> mFrameCompleteCallback;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index c7d7a17a23eb..02257db9df6a 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -90,6 +90,7 @@ EglManager::EglManager()
, mEglConfig(nullptr)
, mEglConfigF16(nullptr)
, mEglConfig1010102(nullptr)
+ , mEglConfigA8(nullptr)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
, mCurrentSurface(EGL_NO_SURFACE)
@@ -246,6 +247,50 @@ EGLConfig EglManager::loadFP16Config(EGLDisplay display, SwapBehavior swapBehavi
return config;
}
+EGLConfig EglManager::loadA8Config(EGLDisplay display, EglManager::SwapBehavior swapBehavior) {
+ EGLint eglSwapBehavior =
+ (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+ EGLint attribs[] = {EGL_RENDERABLE_TYPE,
+ EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE,
+ 8,
+ EGL_GREEN_SIZE,
+ 0,
+ EGL_BLUE_SIZE,
+ 0,
+ EGL_ALPHA_SIZE,
+ 0,
+ EGL_DEPTH_SIZE,
+ 0,
+ EGL_SURFACE_TYPE,
+ EGL_WINDOW_BIT | eglSwapBehavior,
+ EGL_NONE};
+ EGLint numConfigs = 1;
+ if (!eglChooseConfig(display, attribs, nullptr, numConfigs, &numConfigs)) {
+ return EGL_NO_CONFIG_KHR;
+ }
+
+ std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
+ if (!eglChooseConfig(display, attribs, configs.data(), numConfigs, &numConfigs)) {
+ return EGL_NO_CONFIG_KHR;
+ }
+
+ // The component sizes passed to eglChooseConfig are minimums, so configs
+ // contains entries that exceed them. Choose one that matches the sizes
+ // exactly.
+ for (EGLConfig config : configs) {
+ EGLint r{0}, g{0}, b{0}, a{0};
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+ if (8 == r && 0 == g && 0 == b && 0 == a) {
+ return config;
+ }
+ }
+ return EGL_NO_CONFIG_KHR;
+}
+
void EglManager::initExtensions() {
auto extensions = StringUtils::split(eglQueryString(mEglDisplay, EGL_EXTENSIONS));
@@ -345,10 +390,14 @@ Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window,
sk_sp<SkColorSpace> colorSpace) {
LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
- if (!mHasWideColorGamutSupport || !EglExtensions.noConfigContext) {
+ if (!EglExtensions.noConfigContext) {
+ // The caller shouldn't use A8 if we cannot switch modes.
+ LOG_ALWAYS_FATAL_IF(colorMode == ColorMode::A8,
+ "Cannot use A8 without EGL_KHR_no_config_context!");
+
+ // Cannot switch modes without EGL_KHR_no_config_context.
colorMode = ColorMode::Default;
}
-
// The color space we want to use depends on whether linear blending is turned
// on and whether the app has requested wide color gamut rendering. When wide
// color gamut rendering is off, the app simply renders in the display's native
@@ -374,42 +423,61 @@ Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window,
EGLint attribs[] = {EGL_NONE, EGL_NONE, EGL_NONE};
EGLConfig config = mEglConfig;
- if (DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType) {
- if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
+ if (colorMode == ColorMode::A8) {
+ // A8 doesn't use a color space
+ if (!mEglConfigA8) {
+ mEglConfigA8 = loadA8Config(mEglDisplay, mSwapBehavior);
+ LOG_ALWAYS_FATAL_IF(!mEglConfigA8,
+ "Requested ColorMode::A8, but EGL lacks support! error = %s",
+ eglErrorString());
+ }
+ config = mEglConfigA8;
+ } else {
+ if (!mHasWideColorGamutSupport) {
colorMode = ColorMode::Default;
- } else {
- config = mEglConfigF16;
}
- }
- if (EglExtensions.glColorSpace) {
- attribs[0] = EGL_GL_COLORSPACE_KHR;
- switch (colorMode) {
- case ColorMode::Default:
- attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
- break;
- case ColorMode::WideColorGamut: {
- skcms_Matrix3x3 colorGamut;
- LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
- "Could not get gamut matrix from color space");
- if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) {
- attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
- } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
- attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
- } else if (memcmp(&colorGamut, &SkNamedGamut::kRec2020, sizeof(colorGamut)) == 0) {
- attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
- } else {
- LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+
+ if (DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType) {
+ if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
+ colorMode = ColorMode::Default;
+ } else {
+ config = mEglConfigF16;
+ }
+ }
+ if (EglExtensions.glColorSpace) {
+ attribs[0] = EGL_GL_COLORSPACE_KHR;
+ switch (colorMode) {
+ case ColorMode::Default:
+ attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
+ break;
+ case ColorMode::WideColorGamut: {
+ skcms_Matrix3x3 colorGamut;
+ LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
+ "Could not get gamut matrix from color space");
+ if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) {
+ attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+ } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
+ attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
+ } else if (memcmp(&colorGamut, &SkNamedGamut::kRec2020, sizeof(colorGamut)) ==
+ 0) {
+ attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+ } else {
+ LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+ }
+ break;
}
- break;
+ case ColorMode::Hdr:
+ config = mEglConfigF16;
+ attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+ break;
+ case ColorMode::Hdr10:
+ config = mEglConfig1010102;
+ attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+ break;
+ case ColorMode::A8:
+ LOG_ALWAYS_FATAL("Unreachable: A8 doesn't use a color space");
+ break;
}
- case ColorMode::Hdr:
- config = mEglConfigF16;
- attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
- break;
- case ColorMode::Hdr10:
- config = mEglConfig1010102;
- attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
- break;
}
}
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 69f3ed014c53..fc6b28d2e1ad 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -89,6 +89,7 @@ private:
static EGLConfig load8BitsConfig(EGLDisplay display, SwapBehavior swapBehavior);
static EGLConfig loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior);
static EGLConfig load1010102Config(EGLDisplay display, SwapBehavior swapBehavior);
+ static EGLConfig loadA8Config(EGLDisplay display, SwapBehavior swapBehavior);
void initExtensions();
void createPBufferSurface();
@@ -100,6 +101,7 @@ private:
EGLConfig mEglConfig;
EGLConfig mEglConfigF16;
EGLConfig mEglConfig1010102;
+ EGLConfig mEglConfigA8;
EGLContext mEglContext;
EGLSurface mPBufferSurface;
EGLSurface mCurrentSurface;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 72d4ac5081e6..026699c63e16 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -257,10 +257,15 @@ uint32_t RenderProxy::frameTimePercentile(int percentile) {
});
}
-void RenderProxy::dumpGraphicsMemory(int fd, bool includeProfileData) {
+void RenderProxy::dumpGraphicsMemory(int fd, bool includeProfileData, bool resetProfile) {
if (RenderThread::hasInstance()) {
auto& thread = RenderThread::getInstance();
- thread.queue().runSync([&]() { thread.dumpGraphicsMemory(fd, includeProfileData); });
+ thread.queue().runSync([&]() {
+ thread.dumpGraphicsMemory(fd, includeProfileData);
+ if (resetProfile) {
+ thread.globalProfileData()->reset();
+ }
+ });
}
}
@@ -322,7 +327,8 @@ void RenderProxy::setPrepareSurfaceControlForWebviewCallback(
[this, cb = callback]() { mContext->setPrepareSurfaceControlForWebviewCallback(cb); });
}
-void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) {
+void RenderProxy::setFrameCallback(
+ std::function<std::function<void(bool)>(int32_t, int64_t)>&& callback) {
mDrawFrameTask.setFrameCallback(std::move(callback));
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 6417b38df064..491dbd72a14d 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -108,7 +108,8 @@ public:
// Not exported, only used for testing
void resetProfileInfo();
uint32_t frameTimePercentile(int p);
- static void dumpGraphicsMemory(int fd, bool includeProfileData = true);
+ static void dumpGraphicsMemory(int fd, bool includeProfileData = true,
+ bool resetProfile = false);
static void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
static void rotateProcessStatsBuffer();
@@ -123,7 +124,7 @@ public:
void setASurfaceTransactionCallback(
const std::function<bool(int64_t, int64_t, int64_t)>& callback);
void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback);
- void setFrameCallback(std::function<void(int64_t)>&& callback);
+ void setFrameCallback(std::function<std::function<void(bool)>(int32_t, int64_t)>&& callback);
void setFrameCommitCallback(std::function<void(bool)>&& callback);
void setFrameCompleteCallback(std::function<void()>&& callback);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index f83c0a4926f9..f627a3c656d1 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,8 +16,21 @@
#include "RenderThread.h"
+#include <GrContextOptions.h>
+#include <android-base/properties.h>
+#include <dlfcn.h>
+#include <gl/GrGLInterface.h>
#include <gui/TraceUtils.h>
+#include <sys/resource.h>
+#include <ui/FatVector.h>
+#include <utils/Condition.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+
+#include <thread>
+
#include "../HardwareBitmapUploader.h"
+#include "CacheManager.h"
#include "CanvasContext.h"
#include "DeviceInfo.h"
#include "EglManager.h"
@@ -31,19 +44,6 @@
#include "renderstate/RenderState.h"
#include "utils/TimeUtils.h"
-#include <GrContextOptions.h>
-#include <gl/GrGLInterface.h>
-
-#include <dlfcn.h>
-#include <sys/resource.h>
-#include <utils/Condition.h>
-#include <utils/Log.h>
-#include <utils/Mutex.h>
-#include <thread>
-
-#include <android-base/properties.h>
-#include <ui/FatVector.h>
-
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -112,18 +112,31 @@ ASurfaceControlFunctions::ASurfaceControlFunctions() {
"Failed to find required symbol ASurfaceTransaction_setZOrder!");
}
-void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
+void RenderThread::extendedFrameCallback(const AChoreographerFrameCallbackData* cbData,
+ void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
- int64_t vsyncId = AChoreographer_getVsyncId(rt->mChoreographer);
- int64_t frameDeadline = AChoreographer_getFrameDeadline(rt->mChoreographer);
+ size_t preferredFrameTimelineIndex =
+ AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(cbData);
+ AVsyncId vsyncId = AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+ cbData, preferredFrameTimelineIndex);
+ int64_t frameDeadline = AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
+ cbData, preferredFrameTimelineIndex);
+ int64_t frameTimeNanos = AChoreographerFrameCallbackData_getFrameTimeNanos(cbData);
+ // TODO(b/193273294): Remove when shared memory in use w/ expected present time always current.
int64_t frameInterval = AChoreographer_getFrameInterval(rt->mChoreographer);
- rt->mVsyncRequested = false;
- if (rt->timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId, frameDeadline,
- frameInterval) && !rt->mFrameCallbackTaskPending) {
+ rt->frameCallback(vsyncId, frameDeadline, frameTimeNanos, frameInterval);
+}
+
+void RenderThread::frameCallback(int64_t vsyncId, int64_t frameDeadline, int64_t frameTimeNanos,
+ int64_t frameInterval) {
+ mVsyncRequested = false;
+ if (timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId, frameDeadline,
+ frameInterval) &&
+ !mFrameCallbackTaskPending) {
ATRACE_NAME("queue mFrameCallbackTask");
- rt->mFrameCallbackTaskPending = true;
- nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay);
- rt->queue().postAt(runAt, [=]() { rt->dispatchFrameCallbacks(); });
+ mFrameCallbackTaskPending = true;
+ nsecs_t runAt = (frameTimeNanos + mDispatchFrameDelay);
+ queue().postAt(runAt, [=]() { dispatchFrameCallbacks(); });
}
}
@@ -139,8 +152,8 @@ public:
ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
virtual void requestNextVsync() override {
- AChoreographer_postFrameCallback64(mRenderThread->mChoreographer,
- RenderThread::frameCallback, mRenderThread);
+ AChoreographer_postExtendedFrameCallback(
+ mRenderThread->mChoreographer, RenderThread::extendedFrameCallback, mRenderThread);
}
virtual void drainPendingEvents() override {
@@ -157,12 +170,16 @@ public:
virtual void requestNextVsync() override {
mRenderThread->queue().postDelayed(16_ms, [this]() {
- RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread);
+ mRenderThread->frameCallback(UiFrameInfoBuilder::INVALID_VSYNC_ID,
+ std::numeric_limits<int64_t>::max(),
+ systemTime(SYSTEM_TIME_MONOTONIC), 16_ms);
});
}
virtual void drainPendingEvents() override {
- RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread);
+ mRenderThread->frameCallback(UiFrameInfoBuilder::INVALID_VSYNC_ID,
+ std::numeric_limits<int64_t>::max(),
+ systemTime(SYSTEM_TIME_MONOTONIC), 16_ms);
}
private:
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 05d225b856db..c1f6790b25b2 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -83,8 +83,9 @@ typedef ASurfaceControl* (*ASC_create)(ASurfaceControl* parent, const char* debu
typedef void (*ASC_acquire)(ASurfaceControl* control);
typedef void (*ASC_release)(ASurfaceControl* control);
-typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, void* context,
- ASurfaceControl_SurfaceStatsListener func);
+typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, int32_t id,
+ void* context,
+ ASurfaceControl_SurfaceStatsListener func);
typedef void (*ASC_unregisterSurfaceStatsListener)(void* context,
ASurfaceControl_SurfaceStatsListener func);
@@ -210,7 +211,9 @@ private:
// corresponding callbacks for each display event type
static int choreographerCallback(int fd, int events, void* data);
// Callback that will be run on vsync ticks.
- static void frameCallback(int64_t frameTimeNanos, void* data);
+ static void extendedFrameCallback(const AChoreographerFrameCallbackData* cbData, void* data);
+ void frameCallback(int64_t vsyncId, int64_t frameDeadline, int64_t frameTimeNanos,
+ int64_t frameInterval);
// Callback that will be run whenver there is a refresh rate change.
static void refreshRateCallback(int64_t vsyncPeriod, void* data);
void drainDisplayEventQueue();
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 9e8a1e141fe1..a9ff2c60fdbe 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -35,6 +35,9 @@
#include "pipeline/skia/ShaderCache.h"
#include "renderstate/RenderState.h"
+#undef LOG_TAG
+#define LOG_TAG "VulkanManager"
+
namespace android {
namespace uirenderer {
namespace renderthread {
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index fe9a30a59870..7dd3561cb220 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -24,6 +24,9 @@
#include "VulkanManager.h"
#include "utils/Color.h"
+#undef LOG_TAG
+#define LOG_TAG "VulkanSurface"
+
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -197,8 +200,9 @@ bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode
outWindowInfo->bufferFormat = ColorTypeToBufferFormat(colorType);
outWindowInfo->colorspace = colorSpace;
outWindowInfo->dataspace = ColorSpaceToADataSpace(colorSpace.get(), colorType);
- LOG_ALWAYS_FATAL_IF(outWindowInfo->dataspace == HAL_DATASPACE_UNKNOWN,
- "Unsupported colorspace");
+ LOG_ALWAYS_FATAL_IF(
+ outWindowInfo->dataspace == HAL_DATASPACE_UNKNOWN && colorType != kAlpha_8_SkColorType,
+ "Unsupported colorspace");
VkFormat vkPixelFormat;
switch (colorType) {
@@ -211,6 +215,9 @@ bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode
case kRGBA_1010102_SkColorType:
vkPixelFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
break;
+ case kAlpha_8_SkColorType:
+ vkPixelFormat = VK_FORMAT_R8_UNORM;
+ break;
default:
LOG_ALWAYS_FATAL("Unsupported colorType: %d", (int)colorType);
}
@@ -426,7 +433,7 @@ VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
if (bufferInfo->skSurface.get() == nullptr) {
bufferInfo->skSurface = SkSurface::MakeFromAHardwareBuffer(
mGrContext, ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
- kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, nullptr);
+ kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, nullptr, /*from_window=*/true);
if (bufferInfo->skSurface.get() == nullptr) {
ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer,
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index e8ba15fe92af..491af4336f97 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -74,7 +74,7 @@ sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
layerUpdater->setTransform(&transform);
// updateLayer so it's ready to draw
- layerUpdater->updateLayer(true, SkMatrix::I(), nullptr);
+ layerUpdater->updateLayer(true, nullptr, 0, SkRect::MakeEmpty());
return layerUpdater;
}
diff --git a/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp b/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
new file mode 100644
index 000000000000..1e343c1dd283
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include "TestSceneBase.h"
+
+class PathClippingAnimation : public TestScene {
+public:
+ int mSpacing, mSize;
+ bool mClip, mAnimateClip;
+ int mMaxCards;
+ std::vector<sp<RenderNode> > cards;
+
+ PathClippingAnimation(int spacing, int size, bool clip, bool animateClip, int maxCards)
+ : mSpacing(spacing)
+ , mSize(size)
+ , mClip(clip)
+ , mAnimateClip(animateClip)
+ , mMaxCards(maxCards) {}
+
+ PathClippingAnimation(int spacing, int size, bool clip, bool animateClip)
+ : PathClippingAnimation(spacing, size, clip, animateClip, INT_MAX) {}
+
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
+ canvas.enableZ(true);
+ int ci = 0;
+ int numCards = 0;
+
+ for (int x = 0; x < width; x += mSpacing) {
+ for (int y = 0; y < height; y += mSpacing) {
+ auto color = BrightColors[ci++ % BrightColorsCount];
+ auto card = TestUtils::createNode(
+ x, y, x + mSize, y + mSize, [&](RenderProperties& props, Canvas& canvas) {
+ canvas.drawColor(color, SkBlendMode::kSrcOver);
+ if (mClip) {
+ // Create circular path that rounds around the inside of all
+ // four corners of the given square defined by mSize*mSize
+ SkPath path = setPath(mSize);
+ props.mutableOutline().setPath(&path, 1);
+ props.mutableOutline().setShouldClip(true);
+ }
+ });
+ canvas.drawRenderNode(card.get());
+ cards.push_back(card);
+ ++numCards;
+ if (numCards >= mMaxCards) {
+ break;
+ }
+ }
+ if (numCards >= mMaxCards) {
+ break;
+ }
+ }
+
+ canvas.enableZ(false);
+ }
+
+ SkPath setPath(int size) {
+ SkPath path;
+ path.moveTo(0, size / 2);
+ path.cubicTo(0, size * .75, size * .25, size, size / 2, size);
+ path.cubicTo(size * .75, size, size, size * .75, size, size / 2);
+ path.cubicTo(size, size * .25, size * .75, 0, size / 2, 0);
+ path.cubicTo(size / 4, 0, 0, size / 4, 0, size / 2);
+ return path;
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 50;
+ if (curFrame > 25) curFrame = 50 - curFrame;
+ for (auto& card : cards) {
+ if (mAnimateClip) {
+ SkPath path = setPath(mSize - curFrame);
+ card->mutateStagingProperties().mutableOutline().setPath(&path, 1);
+ }
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::DISPLAY_LIST);
+ }
+ }
+};
+
+static TestScene::Registrar _PathClippingUnclipped(TestScene::Info{
+ "pathClipping-unclipped", "Multiple RenderNodes, unclipped.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new PathClippingAnimation(dp(100), dp(80), false, false);
+ }});
+
+static TestScene::Registrar _PathClippingUnclippedSingle(TestScene::Info{
+ "pathClipping-unclippedsingle", "A single RenderNode, unclipped.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new PathClippingAnimation(dp(100), dp(80), false, false, 1);
+ }});
+
+static TestScene::Registrar _PathClippingUnclippedSingleLarge(TestScene::Info{
+ "pathClipping-unclippedsinglelarge", "A single large RenderNode, unclipped.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new PathClippingAnimation(dp(100), dp(350), false, false, 1);
+ }});
+
+static TestScene::Registrar _PathClippingClipped80(TestScene::Info{
+ "pathClipping-clipped80", "Multiple RenderNodes, clipped by paths.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new PathClippingAnimation(dp(100), dp(80), true, false);
+ }});
+
+static TestScene::Registrar _PathClippingClippedSingle(TestScene::Info{
+ "pathClipping-clippedsingle", "A single RenderNode, clipped by a path.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new PathClippingAnimation(dp(100), dp(80), true, false, 1);
+ }});
+
+static TestScene::Registrar _PathClippingClippedSingleLarge(TestScene::Info{
+ "pathClipping-clippedsinglelarge", "A single large RenderNode, clipped by a path.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new PathClippingAnimation(dp(100), dp(350), true, false, 1);
+ }});
+
+static TestScene::Registrar _PathClippingAnimated(TestScene::Info{
+ "pathClipping-animated",
+ "Multiple RenderNodes, clipped by paths which are being altered every frame.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new PathClippingAnimation(dp(100), dp(80), true, true);
+ }});
+
+static TestScene::Registrar _PathClippingAnimatedSingle(TestScene::Info{
+ "pathClipping-animatedsingle",
+ "A single RenderNode, clipped by a path which is being altered every frame.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new PathClippingAnimation(dp(100), dp(80), true, true, 1);
+ }});
+
+static TestScene::Registrar _PathClippingAnimatedSingleLarge(TestScene::Info{
+ "pathClipping-animatedsinglelarge",
+ "A single large RenderNode, clipped by a path which is being altered every frame.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new PathClippingAnimation(dp(100), dp(350), true, true, 1);
+ }});
diff --git a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
index 163745b04ed2..e9f353d887f2 100644
--- a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
@@ -21,14 +21,17 @@
class RoundRectClippingAnimation : public TestScene {
public:
int mSpacing, mSize;
+ int mMaxCards;
- RoundRectClippingAnimation(int spacing, int size) : mSpacing(spacing), mSize(size) {}
+ RoundRectClippingAnimation(int spacing, int size, int maxCards = INT_MAX)
+ : mSpacing(spacing), mSize(size), mMaxCards(maxCards) {}
std::vector<sp<RenderNode> > cards;
void createContent(int width, int height, Canvas& canvas) override {
canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
canvas.enableZ(true);
int ci = 0;
+ int numCards = 0;
for (int x = 0; x < width; x += mSpacing) {
for (int y = 0; y < height; y += mSpacing) {
@@ -42,6 +45,13 @@ public:
});
canvas.drawRenderNode(card.get());
cards.push_back(card);
+ ++numCards;
+ if (numCards >= mMaxCards) {
+ break;
+ }
+ }
+ if (numCards >= mMaxCards) {
+ break;
}
}
@@ -71,3 +81,22 @@ static TestScene::Registrar _RoundRectClippingCpu(TestScene::Info{
[](const TestScene::Options&) -> test::TestScene* {
return new RoundRectClippingAnimation(dp(20), dp(20));
}});
+
+static TestScene::Registrar _RoundRectClippingGrid(TestScene::Info{
+ "roundRectClipping-grid", "A grid of RenderNodes with round rect clipping outlines.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new RoundRectClippingAnimation(dp(100), dp(80));
+ }});
+
+static TestScene::Registrar _RoundRectClippingSingle(TestScene::Info{
+ "roundRectClipping-single", "A single RenderNodes with round rect clipping outline.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new RoundRectClippingAnimation(dp(100), dp(80), 1);
+ }});
+
+static TestScene::Registrar _RoundRectClippingSingleLarge(TestScene::Info{
+ "roundRectClipping-singlelarge",
+ "A single large RenderNodes with round rect clipping outline.",
+ [](const TestScene::Options&) -> test::TestScene* {
+ return new RoundRectClippingAnimation(dp(100), dp(350), 1);
+ }});
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 10ba07905c45..31a8ae1d38cd 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -49,7 +49,7 @@ public:
paint.setAntiAlias(true);
paint.setColor(Color::Green_700);
canvas.drawCircle(200, 200, 200, paint);
- SkPaint alphaPaint;
+ Paint alphaPaint;
alphaPaint.setAlpha(128);
canvas.restoreUnclippedLayer(unclippedSaveLayer, alphaPaint);
canvas.restore();
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index 955a5e7d8b3a..0c389bfe8b71 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -36,19 +36,16 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight());
EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter());
EXPECT_FALSE(layerUpdater->backingLayer()->isBlend());
- EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform());
// push the deferred updates to the layer
- SkMatrix scaledMatrix = SkMatrix::Scale(0.5, 0.5);
SkBitmap bitmap;
bitmap.allocN32Pixels(16, 16);
sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap);
- layerUpdater->updateLayer(true, scaledMatrix, layerImage);
+ layerUpdater->updateLayer(true, layerImage, 0, SkRect::MakeEmpty());
// the backing layer should now have all the properties applied.
EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth());
EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight());
EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter());
EXPECT_TRUE(layerUpdater->backingLayer()->isBlend());
- EXPECT_EQ(scaledMatrix, layerUpdater->backingLayer()->getTexTransform());
}
diff --git a/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp b/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp
new file mode 100644
index 000000000000..571a26707c93
--- /dev/null
+++ b/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <FrameMetricsObserver.h>
+#include <FrameMetricsReporter.h>
+#include <utils/TimeUtils.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+using ::testing::NotNull;
+
+class TestFrameMetricsObserver : public FrameMetricsObserver {
+public:
+ explicit TestFrameMetricsObserver(bool waitForPresentTime)
+ : FrameMetricsObserver(waitForPresentTime){};
+
+ MOCK_METHOD(void, notify, (const int64_t* buffer), (override));
+};
+
+// To make sure it is clear that something went wrong if no from frame is set (to make it easier
+// to catch bugs were we forget to set the fromFrame).
+TEST(FrameMetricsReporter, doesNotReportAnyFrameIfNoFromFrameIsSpecified) {
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer = sp<TestFrameMetricsObserver>::make(false /*waitForPresentTime*/);
+ EXPECT_CALL(*observer, notify).Times(0);
+
+ reporter->addObserver(observer.get());
+
+ const int64_t* stats;
+ bool hasPresentTime = false;
+ uint64_t frameNumber = 1;
+ int32_t surfaceControlId = 0;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ frameNumber = 10;
+ surfaceControlId = 0;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ frameNumber = 0;
+ surfaceControlId = 2;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ frameNumber = 10;
+ surfaceControlId = 2;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, respectsWaitForPresentTimeUnset) {
+ const int64_t* stats;
+ bool hasPresentTime = false;
+ uint64_t frameNumber = 3;
+ int32_t surfaceControlId = 0;
+
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer = sp<TestFrameMetricsObserver>::make(hasPresentTime);
+ observer->reportMetricsFrom(frameNumber, surfaceControlId);
+ reporter->addObserver(observer.get());
+
+ EXPECT_CALL(*observer, notify).Times(1);
+ hasPresentTime = false;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ EXPECT_CALL(*observer, notify).Times(0);
+ hasPresentTime = true;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, respectsWaitForPresentTimeSet) {
+ const int64_t* stats;
+ bool hasPresentTime = true;
+ uint64_t frameNumber = 3;
+ int32_t surfaceControlId = 0;
+
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer = sp<TestFrameMetricsObserver>::make(hasPresentTime);
+ observer->reportMetricsFrom(frameNumber, surfaceControlId);
+ reporter->addObserver(observer.get());
+
+ EXPECT_CALL(*observer, notify).Times(0);
+ hasPresentTime = false;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ EXPECT_CALL(*observer, notify).Times(1);
+ hasPresentTime = true;
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, reportsAllFramesAfterSpecifiedFromFrame) {
+ const int64_t* stats;
+ bool hasPresentTime = false;
+
+ std::vector<uint64_t> frameNumbers{0, 1, 10};
+ std::vector<int32_t> surfaceControlIds{0, 1, 10};
+ for (uint64_t frameNumber : frameNumbers) {
+ for (int32_t surfaceControlId : surfaceControlIds) {
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer =
+ sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+ observer->reportMetricsFrom(frameNumber, surfaceControlId);
+ reporter->addObserver(observer.get());
+
+ EXPECT_CALL(*observer, notify).Times(8);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 1, surfaceControlId);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10, surfaceControlId);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId + 1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber - 1,
+ surfaceControlId + 1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 1,
+ surfaceControlId + 1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10,
+ surfaceControlId + 1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10,
+ surfaceControlId + 10);
+ }
+ }
+}
+
+TEST(FrameMetricsReporter, doesNotReportsFramesBeforeSpecifiedFromFrame) {
+ const int64_t* stats;
+ bool hasPresentTime = false;
+
+ std::vector<uint64_t> frameNumbers{1, 10};
+ std::vector<int32_t> surfaceControlIds{0, 1, 10};
+ for (uint64_t frameNumber : frameNumbers) {
+ for (int32_t surfaceControlId : surfaceControlIds) {
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer =
+ sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+ observer->reportMetricsFrom(frameNumber, surfaceControlId);
+ reporter->addObserver(observer.get());
+
+ EXPECT_CALL(*observer, notify).Times(0);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber - 1, surfaceControlId);
+ if (surfaceControlId > 0) {
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber,
+ surfaceControlId - 1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber - 1,
+ surfaceControlId - 1);
+ }
+ }
+ }
+}
+
+TEST(FrameMetricsReporter, canRemoveObservers) {
+ const int64_t* stats;
+ bool hasPresentTime = false;
+ uint64_t frameNumber = 3;
+ int32_t surfaceControlId = 0;
+
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer = sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+
+ observer->reportMetricsFrom(frameNumber, surfaceControlId);
+ reporter->addObserver(observer.get());
+
+ EXPECT_CALL(*observer, notify).Times(1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ ASSERT_TRUE(reporter->removeObserver(observer.get()));
+
+ EXPECT_CALL(*observer, notify).Times(0);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, canSupportMultipleObservers) {
+ const int64_t* stats;
+ bool hasPresentTime = false;
+ uint64_t frameNumber = 3;
+ int32_t surfaceControlId = 0;
+
+ auto reporter = std::make_shared<FrameMetricsReporter>();
+
+ auto observer1 = sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+ auto observer2 = sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+ observer1->reportMetricsFrom(frameNumber, surfaceControlId);
+ observer2->reportMetricsFrom(frameNumber + 10, surfaceControlId + 1);
+ reporter->addObserver(observer1.get());
+ reporter->addObserver(observer2.get());
+
+ EXPECT_CALL(*observer1, notify).Times(1);
+ EXPECT_CALL(*observer2, notify).Times(0);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+ EXPECT_CALL(*observer1, notify).Times(1);
+ EXPECT_CALL(*observer2, notify).Times(1);
+ reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10, surfaceControlId + 1);
+}
diff --git a/libs/hwui/tests/unit/JankTrackerTests.cpp b/libs/hwui/tests/unit/JankTrackerTests.cpp
index f467ebf5d888..5b397de36a86 100644
--- a/libs/hwui/tests/unit/JankTrackerTests.cpp
+++ b/libs/hwui/tests/unit/JankTrackerTests.cpp
@@ -34,6 +34,9 @@ TEST(JankTracker, noJank) {
JankTracker jankTracker(&container);
std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
FrameInfo* info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
info->set(FrameInfoIndex::Vsync) = 101_ms;
@@ -42,7 +45,7 @@ TEST(JankTracker, noJank) {
info->set(FrameInfoIndex::FrameCompleted) = 115_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 116_ms;
@@ -52,7 +55,7 @@ TEST(JankTracker, noJank) {
info->set(FrameInfoIndex::FrameCompleted) = 131_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(2, container.get()->totalFrameCount());
ASSERT_EQ(0, container.get()->jankFrameCount());
@@ -65,6 +68,9 @@ TEST(JankTracker, jank) {
JankTracker jankTracker(&container);
std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
FrameInfo* info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
info->set(FrameInfoIndex::Vsync) = 101_ms;
@@ -73,7 +79,7 @@ TEST(JankTracker, jank) {
info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->totalFrameCount());
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -85,6 +91,9 @@ TEST(JankTracker, legacyJankButNoRealJank) {
JankTracker jankTracker(&container);
std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
FrameInfo* info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
info->set(FrameInfoIndex::Vsync) = 101_ms;
@@ -93,7 +102,7 @@ TEST(JankTracker, legacyJankButNoRealJank) {
info->set(FrameInfoIndex::FrameCompleted) = 118_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->totalFrameCount());
ASSERT_EQ(0, container.get()->jankFrameCount());
@@ -106,6 +115,9 @@ TEST(JankTracker, doubleStuffed) {
JankTracker jankTracker(&container);
std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
// First frame janks
FrameInfo* info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
@@ -115,7 +127,7 @@ TEST(JankTracker, doubleStuffed) {
info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -128,7 +140,7 @@ TEST(JankTracker, doubleStuffed) {
info->set(FrameInfoIndex::FrameCompleted) = 137_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(2, container.get()->totalFrameCount());
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -140,6 +152,9 @@ TEST(JankTracker, doubleStuffedThenPauseThenJank) {
JankTracker jankTracker(&container);
std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
// First frame janks
FrameInfo* info = jankTracker.startFrame();
info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
@@ -149,7 +164,7 @@ TEST(JankTracker, doubleStuffedThenPauseThenJank) {
info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -162,7 +177,7 @@ TEST(JankTracker, doubleStuffedThenPauseThenJank) {
info->set(FrameInfoIndex::FrameCompleted) = 137_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -175,8 +190,8 @@ TEST(JankTracker, doubleStuffedThenPauseThenJank) {
info->set(FrameInfoIndex::FrameCompleted) = 169_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 168_ms;
- jankTracker.finishFrame(*info, reporter);
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(3, container.get()->totalFrameCount());
ASSERT_EQ(2, container.get()->jankFrameCount());
-} \ No newline at end of file
+}
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 423400eb8ff1..ec949b80ea55 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -469,7 +469,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
}
SkCanvas* onNewCanvas() override { return new ProjectionTestCanvas(mDrawCounter); }
sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
- void onCopyOnWrite(ContentChangeMode) override {}
+ bool onCopyOnWrite(ContentChangeMode) override { return true; }
int* mDrawCounter;
void onWritePixels(const SkPixmap&, int x, int y) {}
};
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index a1ba70a22581..dc1b2e668dd0 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -61,11 +61,11 @@ TEST(SkiaBehavior, lightingColorFilter_simplify) {
TEST(SkiaBehavior, porterDuffCreateIsCached) {
SkPaint paint;
paint.setBlendMode(SkBlendMode::kOverlay);
- auto expected = paint.getBlendMode();
+ auto expected = paint.asBlendMode();
paint.setBlendMode(SkBlendMode::kClear);
- ASSERT_NE(expected, paint.getBlendMode());
+ ASSERT_NE(expected, paint.asBlendMode());
paint.setBlendMode(SkBlendMode::kOverlay);
- ASSERT_EQ(expected, paint.getBlendMode());
+ ASSERT_EQ(expected, paint.asBlendMode());
}
TEST(SkiaBehavior, pathIntersection) {
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 8c999c41bf7b..60ae6044cd5b 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -221,7 +221,7 @@ public:
sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override { return nullptr; }
T* canvas() { return static_cast<T*>(getCanvas()); }
- void onCopyOnWrite(ContentChangeMode) override {}
+ bool onCopyOnWrite(ContentChangeMode) override { return true; }
void onWritePixels(const SkPixmap&, int x, int y) override {}
};
}
@@ -382,7 +382,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
std::vector<sp<RenderNode>> nodes;
nodes.push_back(TestUtils::createSkiaNode(
20, 20, 30, 30, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
- canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated);
+ canvas.replaceClipRect_deprecated(0, -20, 10, 30);
canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
}));
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 5d9f2297c15a..2293ace0bd16 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -57,6 +57,10 @@ static inline SkImageInfo createImageInfo(int32_t width, int32_t height, int32_t
colorType = kRGBA_F16_SkColorType;
alphaType = kPremul_SkAlphaType;
break;
+ case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+ colorType = kAlpha_8_SkColorType;
+ alphaType = kPremul_SkAlphaType;
+ break;
default:
ALOGV("Unsupported format: %d, return unknown by default", format);
break;
@@ -90,6 +94,8 @@ uint32_t ColorTypeToBufferFormat(SkColorType colorType) {
// Hardcoding the value from android::PixelFormat
static constexpr uint64_t kRGBA4444 = 7;
return kRGBA4444;
+ case kAlpha_8_SkColorType:
+ return AHARDWAREBUFFER_FORMAT_R8_UNORM;
default:
ALOGV("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType);
return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
@@ -219,6 +225,7 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
gamut = SkNamedGamut::kSRGB;
break;
case HAL_DATASPACE_STANDARD_BT2020:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
gamut = SkNamedGamut::kRec2020;
break;
case HAL_DATASPACE_STANDARD_DCI_P3:
@@ -233,7 +240,6 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
case HAL_DATASPACE_STANDARD_BT601_525:
case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
- case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
case HAL_DATASPACE_STANDARD_BT470M:
case HAL_DATASPACE_STANDARD_FILM:
default:
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index a8f2d9a28d67..94bcb1110e05 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -32,13 +32,6 @@ namespace uirenderer {
*/
class PaintUtils {
public:
- static inline GLenum getFilter(const SkPaint* paint) {
- if (!paint || paint->getFilterQuality() != kNone_SkFilterQuality) {
- return GL_LINEAR;
- }
- return GL_NEAREST;
- }
-
static bool isOpaquePaint(const SkPaint* paint) {
if (!paint) return true; // default (paintless) behavior is SrcOver, black
@@ -48,7 +41,7 @@ public:
}
// Only let simple srcOver / src blending modes declare opaque, since behavior is clear.
- SkBlendMode mode = paint->getBlendMode();
+ const auto mode = paint->asBlendMode();
return mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc;
}
@@ -59,7 +52,7 @@ public:
}
static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) {
- return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver;
+ return paint ? paint->getBlendMode_or(SkBlendMode::kSrcOver) : SkBlendMode::kSrcOver;
}
static inline int getAlphaDirect(const SkPaint* paint) {
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 55f932dffff1..6c0fd5f65359 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -55,6 +55,7 @@ cc_library_shared {
"-Wall",
"-Wextra",
"-Werror",
+ "-Wthread-safety",
],
}
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 8f04cfb70469..1dc74e5f7740 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -17,24 +17,41 @@
#define LOG_TAG "PointerController"
//#define LOG_NDEBUG 0
-// Log debug messages about pointer updates
-#define DEBUG_POINTER_UPDATES 0
-
#include "PointerController.h"
-#include "MouseCursorController.h"
-#include "PointerControllerContext.h"
-#include "TouchSpotController.h"
-
-#include <log/log.h>
-#include <SkBitmap.h>
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkColor.h>
-#include <SkPaint.h>
+#include <android-base/thread_annotations.h>
+
+#include "PointerControllerContext.h"
namespace android {
+namespace {
+
+const ui::Transform kIdentityTransform;
+
+} // namespace
+
+// --- PointerController::DisplayInfoListener ---
+
+void PointerController::DisplayInfoListener::onWindowInfosChanged(
+ const std::vector<android::gui::WindowInfo>&,
+ const std::vector<android::gui::DisplayInfo>& displayInfos) {
+ std::scoped_lock lock(mLock);
+ if (mPointerController == nullptr) return;
+
+ // PointerController uses DisplayInfoListener's lock.
+ base::ScopedLockAssertion assumeLocked(mPointerController->getLock());
+ mPointerController->onDisplayInfosChangedLocked(displayInfos);
+}
+
+void PointerController::DisplayInfoListener::onPointerControllerDestroyed() {
+ std::scoped_lock lock(mLock);
+ mPointerController = nullptr;
+}
+
// --- PointerController ---
std::shared_ptr<PointerController> PointerController::create(
@@ -63,9 +80,36 @@ std::shared_ptr<PointerController> PointerController::create(
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
const sp<Looper>& looper,
const sp<SpriteController>& spriteController)
- : mContext(policy, looper, spriteController, *this), mCursorController(mContext) {
- std::scoped_lock lock(mLock);
+ : PointerController(
+ policy, looper, spriteController,
+ [](const sp<android::gui::WindowInfosListener>& listener) {
+ SurfaceComposerClient::getDefault()->addWindowInfosListener(listener);
+ },
+ [](const sp<android::gui::WindowInfosListener>& listener) {
+ SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
+ }) {}
+
+PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController,
+ WindowListenerConsumer registerListener,
+ WindowListenerConsumer unregisterListener)
+ : mContext(policy, looper, spriteController, *this),
+ mCursorController(mContext),
+ mDisplayInfoListener(new DisplayInfoListener(this)),
+ mUnregisterWindowInfosListener(std::move(unregisterListener)) {
+ std::scoped_lock lock(getLock());
mLocked.presentation = Presentation::SPOT;
+ registerListener(mDisplayInfoListener);
+}
+
+PointerController::~PointerController() {
+ mDisplayInfoListener->onPointerControllerDestroyed();
+ mUnregisterWindowInfosListener(mDisplayInfoListener);
+}
+
+std::mutex& PointerController::getLock() const {
+ return mDisplayInfoListener->mLock;
}
bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
@@ -74,7 +118,14 @@ bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX
}
void PointerController::move(float deltaX, float deltaY) {
- mCursorController.move(deltaX, deltaY);
+ const int32_t displayId = mCursorController.getDisplayId();
+ vec2 transformed;
+ {
+ std::scoped_lock lock(getLock());
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ transformed = transformWithoutTranslation(transform, {deltaX, deltaY});
+ }
+ mCursorController.move(transformed.x, transformed.y);
}
void PointerController::setButtonState(int32_t buttonState) {
@@ -86,12 +137,26 @@ int32_t PointerController::getButtonState() const {
}
void PointerController::setPosition(float x, float y) {
- std::scoped_lock lock(mLock);
- mCursorController.setPosition(x, y);
+ const int32_t displayId = mCursorController.getDisplayId();
+ vec2 transformed;
+ {
+ std::scoped_lock lock(getLock());
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ transformed = transform.transform(x, y);
+ }
+ mCursorController.setPosition(transformed.x, transformed.y);
}
void PointerController::getPosition(float* outX, float* outY) const {
+ const int32_t displayId = mCursorController.getDisplayId();
mCursorController.getPosition(outX, outY);
+ {
+ std::scoped_lock lock(getLock());
+ const auto& transform = getTransformForDisplayLocked(displayId);
+ const auto xy = transform.inverse().transform(*outX, *outY);
+ *outX = xy.x;
+ *outY = xy.y;
+ }
}
int32_t PointerController::getDisplayId() const {
@@ -99,17 +164,17 @@ int32_t PointerController::getDisplayId() const {
}
void PointerController::fade(Transition transition) {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
mCursorController.fade(transition);
}
void PointerController::unfade(Transition transition) {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
mCursorController.unfade(transition);
}
void PointerController::setPresentation(Presentation presentation) {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
if (mLocked.presentation == presentation) {
return;
@@ -129,20 +194,34 @@ void PointerController::setPresentation(Presentation presentation) {
void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits, int32_t displayId) {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
+ std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
+ const ui::Transform& transform = getTransformForDisplayLocked(displayId);
+
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+ const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
+
+ const vec2 xy = transform.transform(spotCoords[index].getXYValue());
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+ float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+ outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
+ }
+
auto it = mLocked.spotControllers.find(displayId);
if (it == mLocked.spotControllers.end()) {
mLocked.spotControllers.try_emplace(displayId, displayId, mContext);
}
- mLocked.spotControllers.at(displayId).setSpots(spotCoords, spotIdToIndex, spotIdBits);
+ mLocked.spotControllers.at(displayId).setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits);
}
void PointerController::clearSpots() {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
clearSpotsLocked();
}
-void PointerController::clearSpotsLocked() REQUIRES(mLock) {
+void PointerController::clearSpotsLocked() {
for (auto& [displayID, spotController] : mLocked.spotControllers) {
spotController.clearSpots();
}
@@ -153,7 +232,7 @@ void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout
}
void PointerController::reloadPointerResources() {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
for (auto& [displayID, spotController] : mLocked.spotControllers) {
spotController.reloadSpotResources();
@@ -169,7 +248,7 @@ void PointerController::reloadPointerResources() {
}
void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
bool getAdditionalMouseResources = false;
if (mLocked.presentation == PointerController::Presentation::POINTER) {
@@ -179,12 +258,12 @@ void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
}
void PointerController::updatePointerIcon(int32_t iconId) {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
mCursorController.updatePointerIcon(iconId);
}
void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
mCursorController.setCustomPointerIcon(icon);
}
@@ -194,11 +273,11 @@ void PointerController::doInactivityTimeout() {
void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) {
std::unordered_set<int32_t> displayIdSet;
- for (DisplayViewport viewport : viewports) {
+ for (const DisplayViewport& viewport : viewports) {
displayIdSet.insert(viewport.displayId);
}
- std::scoped_lock lock(mLock);
+ std::scoped_lock lock(getLock());
for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) {
int32_t displayID = it->first;
if (!displayIdSet.count(displayID)) {
@@ -214,4 +293,17 @@ void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>&
}
}
+void PointerController::onDisplayInfosChangedLocked(
+ const std::vector<gui::DisplayInfo>& displayInfo) {
+ mLocked.mDisplayInfos = displayInfo;
+}
+
+const ui::Transform& PointerController::getTransformForDisplayLocked(int displayId) const {
+ const auto& di = mLocked.mDisplayInfos;
+ auto it = std::find_if(di.begin(), di.end(), [displayId](const gui::DisplayInfo& info) {
+ return info.displayId == displayId;
+ });
+ return it != di.end() ? it->transform : kIdentityTransform;
+}
+
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 97567bab202b..2e6e851ee15a 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -47,7 +47,7 @@ public:
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
const sp<SpriteController>& spriteController);
- virtual ~PointerController() = default;
+ ~PointerController() override;
virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
virtual void move(float deltaX, float deltaY);
@@ -72,11 +72,31 @@ public:
void reloadPointerResources();
void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
+ void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos)
+ REQUIRES(getLock());
+
+protected:
+ using WindowListenerConsumer =
+ std::function<void(const sp<android::gui::WindowInfosListener>&)>;
+
+ // Constructor used to test WindowInfosListener registration.
+ PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController,
+ WindowListenerConsumer registerListener,
+ WindowListenerConsumer unregisterListener);
+
private:
+ PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+ const sp<SpriteController>& spriteController);
+
friend PointerControllerContext::LooperCallback;
friend PointerControllerContext::MessageHandler;
- mutable std::mutex mLock;
+ // PointerController's DisplayInfoListener can outlive the PointerController because when the
+ // listener is registered, a strong pointer to the listener (which can extend its lifecycle)
+ // is given away. To avoid the small overhead of using two separate locks in these two objects,
+ // we use the DisplayInfoListener's lock in PointerController.
+ std::mutex& getLock() const;
PointerControllerContext mContext;
@@ -85,12 +105,30 @@ private:
struct Locked {
Presentation presentation;
+ std::vector<gui::DisplayInfo> mDisplayInfos;
std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers;
- } mLocked GUARDED_BY(mLock);
+ } mLocked GUARDED_BY(getLock());
- PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- const sp<SpriteController>& spriteController);
- void clearSpotsLocked();
+ class DisplayInfoListener : public gui::WindowInfosListener {
+ public:
+ explicit DisplayInfoListener(PointerController* pc) : mPointerController(pc){};
+ void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>&,
+ const std::vector<android::gui::DisplayInfo>&) override;
+ void onPointerControllerDestroyed();
+
+ // This lock is also used by PointerController. See PointerController::getLock().
+ std::mutex mLock;
+
+ private:
+ PointerController* mPointerController GUARDED_BY(mLock);
+ };
+
+ sp<DisplayInfoListener> mDisplayInfoListener;
+ const WindowListenerConsumer mUnregisterWindowInfosListener;
+
+ const ui::Transform& getTransformForDisplayLocked(int displayId) const REQUIRES(getLock());
+
+ void clearSpotsLocked() REQUIRES(getLock());
};
} // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index d10e68816d28..2b809eab4ae4 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -27,10 +27,12 @@ namespace android {
// --- SpriteController ---
-SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLayer) :
- mLooper(looper), mOverlayLayer(overlayLayer) {
+SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLayer,
+ ParentSurfaceProvider parentSurfaceProvider)
+ : mLooper(looper),
+ mOverlayLayer(overlayLayer),
+ mParentSurfaceProvider(std::move(parentSurfaceProvider)) {
mHandler = new WeakMessageHandler(this);
-
mLocked.transactionNestingCount = 0;
mLocked.deferredSpriteUpdate = false;
}
@@ -68,8 +70,8 @@ void SpriteController::closeTransaction() {
}
void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
- bool wasEmpty = mLocked.invalidatedSprites.isEmpty();
- mLocked.invalidatedSprites.push(sprite);
+ bool wasEmpty = mLocked.invalidatedSprites.empty();
+ mLocked.invalidatedSprites.push_back(sprite);
if (wasEmpty) {
if (mLocked.transactionNestingCount != 0) {
mLocked.deferredSpriteUpdate = true;
@@ -80,8 +82,8 @@ void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) {
}
void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl) {
- bool wasEmpty = mLocked.disposedSurfaces.isEmpty();
- mLocked.disposedSurfaces.push(surfaceControl);
+ bool wasEmpty = mLocked.disposedSurfaces.empty();
+ mLocked.disposedSurfaces.push_back(surfaceControl);
if (wasEmpty) {
mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES));
}
@@ -111,7 +113,7 @@ void SpriteController::doUpdateSprites() {
numSprites = mLocked.invalidatedSprites.size();
for (size_t i = 0; i < numSprites; i++) {
- const sp<SpriteImpl>& sprite = mLocked.invalidatedSprites.itemAt(i);
+ const sp<SpriteImpl>& sprite = mLocked.invalidatedSprites[i];
updates.push(SpriteUpdate(sprite, sprite->getStateLocked()));
sprite->resetDirtyLocked();
@@ -168,7 +170,7 @@ 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) {
- t.setLayerStack(update.state.surfaceControl, update.state.displayId);
+ t.reparent(update.state.surfaceControl, mParentSurfaceProvider(update.state.displayId));
needApplyTransaction = true;
}
}
@@ -303,7 +305,7 @@ void SpriteController::doUpdateSprites() {
void SpriteController::doDisposeSurfaces() {
// Collect disposed surfaces.
- Vector<sp<SurfaceControl> > disposedSurfaces;
+ std::vector<sp<SurfaceControl>> disposedSurfaces;
{ // acquire lock
AutoMutex _l(mLock);
@@ -311,6 +313,13 @@ void SpriteController::doDisposeSurfaces() {
mLocked.disposedSurfaces.clear();
} // release lock
+ // Remove the parent from all surfaces.
+ SurfaceComposerClient::Transaction t;
+ for (const sp<SurfaceControl>& sc : disposedSurfaces) {
+ t.reparent(sc, nullptr);
+ }
+ t.apply();
+
// Release the last reference to each surface outside of the lock.
// We don't want the surfaces to be deleted while we are holding our lock.
disposedSurfaces.clear();
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 137b5646feae..2e9cb9685c46 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -114,7 +114,8 @@ protected:
virtual ~SpriteController();
public:
- SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
+ using ParentSurfaceProvider = std::function<sp<SurfaceControl>(int /*displayId*/)>;
+ SpriteController(const sp<Looper>& looper, int32_t overlayLayer, ParentSurfaceProvider parent);
/* Creates a new sprite, initially invisible. */
virtual sp<Sprite> createSprite();
@@ -245,12 +246,13 @@ private:
sp<Looper> mLooper;
const int32_t mOverlayLayer;
sp<WeakMessageHandler> mHandler;
+ ParentSurfaceProvider mParentSurfaceProvider;
sp<SurfaceComposerClient> mSurfaceComposerClient;
struct Locked {
- Vector<sp<SpriteImpl> > invalidatedSprites;
- Vector<sp<SurfaceControl> > disposedSurfaces;
+ std::vector<sp<SpriteImpl>> invalidatedSprites;
+ std::vector<sp<SurfaceControl>> disposedSurfaces;
uint32_t transactionNestingCount;
bool deferredSpriteUpdate;
} mLocked; // guarded by mLock
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index b67088a389b6..dae1fccec804 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -255,4 +255,36 @@ TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
ensureDisplayViewportIsSet();
}
+class PointerControllerWindowInfoListenerTest : public Test {};
+
+class TestPointerController : public PointerController {
+public:
+ TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
+ const sp<Looper>& looper)
+ : PointerController(
+ new MockPointerControllerPolicyInterface(), looper,
+ new NiceMock<MockSpriteController>(looper),
+ [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
+ // Register listener
+ registeredListener = listener;
+ },
+ [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
+ // Unregister listener
+ if (registeredListener == listener) registeredListener = nullptr;
+ }) {}
+};
+
+TEST_F(PointerControllerWindowInfoListenerTest,
+ doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
+ sp<android::gui::WindowInfosListener> registeredListener;
+ sp<android::gui::WindowInfosListener> localListenerCopy;
+ {
+ TestPointerController pointerController(registeredListener, new Looper(false));
+ ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
+ localListenerCopy = registeredListener;
+ }
+ EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
+ localListenerCopy->onWindowInfosChanged({}, {});
+}
+
} // namespace android
diff --git a/libs/input/tests/mocks/MockSpriteController.h b/libs/input/tests/mocks/MockSpriteController.h
index a034f66c9abf..62f1d65e77a5 100644
--- a/libs/input/tests/mocks/MockSpriteController.h
+++ b/libs/input/tests/mocks/MockSpriteController.h
@@ -26,7 +26,8 @@ namespace android {
class MockSpriteController : public SpriteController {
public:
- MockSpriteController(sp<Looper> looper) : SpriteController(looper, 0) {}
+ MockSpriteController(sp<Looper> looper)
+ : SpriteController(looper, 0, [](int) { return nullptr; }) {}
~MockSpriteController() {}
MOCK_METHOD(sp<Sprite>, createSprite, (), (override));
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
index bf2e764aae6a..f656ebfc3b77 100644
--- a/libs/services/Android.bp
+++ b/libs/services/Android.bp
@@ -27,6 +27,7 @@ cc_library_shared {
name: "libservices",
srcs: [
":IDropBoxManagerService.aidl",
+ ":ILogcatManagerService_aidl",
"src/content/ComponentName.cpp",
"src/os/DropBoxManager.cpp",
],