summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Shell/OWNERS1
-rw-r--r--libs/WindowManager/Shell/res/layout/split_divider.xml27
-rw-r--r--libs/WindowManager/Shell/res/raw/wm_shell_protolog.json72
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml78
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings_tv.xml25
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml78
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings_tv.xml25
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml78
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings_tv.xml25
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml72
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings_tv.xml24
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/LetterboxTaskListener.java110
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java231
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java59
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java243
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java224
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java185
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java56
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxConfigController.java108
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java226
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java)265
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java61
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java141
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java157
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java17
-rw-r--r--libs/WindowManager/Shell/tests/OWNERS2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt46
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt223
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt40
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt58
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt7
-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/helpers/SplitScreenHelper.kt64
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt87
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt25
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt51
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt159
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt138
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt36
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/Android.bp0
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml23
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml43
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java39
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenActivity.java29
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java28
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/LetterboxTaskListenerTest.java125
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java102
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java106
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java75
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java37
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java34
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestRunningTaskInfoBuilder.java43
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxConfigControllerTest.java131
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java320
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java (renamed from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java)198
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java237
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java26
-rw-r--r--libs/androidfw/Android.bp10
-rwxr-xr-xlibs/androidfw/ApkAssets.cpp82
-rw-r--r--libs/androidfw/Asset.cpp174
-rw-r--r--libs/androidfw/AssetManager.cpp16
-rw-r--r--libs/androidfw/AssetManager2.cpp685
-rw-r--r--libs/androidfw/AttributeResolution.cpp450
-rw-r--r--libs/androidfw/ChunkIterator.cpp27
-rw-r--r--libs/androidfw/ConfigDescription.cpp5
-rw-r--r--libs/androidfw/Idmap.cpp12
-rw-r--r--libs/androidfw/LoadedArsc.cpp339
-rw-r--r--libs/androidfw/ResourceTypes.cpp488
-rw-r--r--libs/androidfw/ResourceUtils.cpp77
-rw-r--r--libs/androidfw/StreamingZipInflater.cpp6
-rw-r--r--libs/androidfw/ZipFileRO.cpp23
-rw-r--r--libs/androidfw/ZipUtils.cpp21
-rw-r--r--libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp5
-rw-r--r--libs/androidfw/include/androidfw/Asset.h108
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h264
-rw-r--r--libs/androidfw/include/androidfw/AttributeResolution.h24
-rw-r--r--libs/androidfw/include/androidfw/Chunk.h23
-rw-r--r--libs/androidfw/include/androidfw/Errors.h47
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h4
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h37
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h70
-rw-r--r--libs/androidfw/include/androidfw/ResourceUtils.h9
-rw-r--r--libs/androidfw/include/androidfw/StreamingZipInflater.h6
-rw-r--r--libs/androidfw/include/androidfw/Util.h8
-rw-r--r--libs/androidfw/include/androidfw/ZipFileRO.h27
-rw-r--r--libs/androidfw/include/androidfw/ZipUtils.h6
-rw-r--r--libs/androidfw/tests/AssetManager2_bench.cpp10
-rw-r--r--libs/androidfw/tests/AssetManager2_test.cpp530
-rw-r--r--libs/androidfw/tests/AttributeResolution_bench.cpp19
-rw-r--r--libs/androidfw/tests/AttributeResolution_test.cpp18
-rw-r--r--libs/androidfw/tests/BenchmarkHelpers.cpp10
-rw-r--r--libs/androidfw/tests/CommonHelpers.cpp5
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp153
-rw-r--r--libs/androidfw/tests/LoadedArsc_test.cpp63
-rw-r--r--libs/androidfw/tests/ResTable_test.cpp26
-rw-r--r--libs/androidfw/tests/TestHelpers.cpp12
-rw-r--r--libs/androidfw/tests/Theme_bench.cpp5
-rw-r--r--libs/androidfw/tests/Theme_test.cpp278
-rw-r--r--libs/hwui/SkiaCanvas.cpp22
-rw-r--r--libs/hwui/SkiaCanvas.h6
-rw-r--r--libs/hwui/canvas/CanvasOpBuffer.h4
-rw-r--r--libs/hwui/canvas/CanvasOpRasterizer.cpp2
-rw-r--r--libs/hwui/canvas/CanvasOpTypes.h6
-rw-r--r--libs/hwui/canvas/CanvasOps.h95
-rw-r--r--libs/hwui/canvas/OpBuffer.h2
-rw-r--r--libs/hwui/hwui/Canvas.h6
-rw-r--r--libs/hwui/jni/Typeface.cpp16
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp14
-rw-r--r--libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp3
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp10
-rw-r--r--libs/hwui/tests/unit/CanvasOpTests.cpp150
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp2
301 files changed, 15250 insertions, 2877 deletions
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
index 36da7aac9106..e2c67fd8f8d4 100644
--- a/libs/WindowManager/Shell/OWNERS
+++ b/libs/WindowManager/Shell/OWNERS
@@ -1,5 +1,4 @@
# sysui owners
hwwang@google.com
-mrenouf@google.com
winsonc@google.com
madym@google.com
diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml
new file mode 100644
index 000000000000..b86f36a14d17
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/split_divider.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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.apppairs.DividerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <View
+ style="@style/DockedDividerBackground"
+ android:id="@+id/docked_divider_background"
+ android:background="@color/docked_divider_background"/>
+
+</com.android.wm.shell.apppairs.DividerView>
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index 6342c00f80fa..db9e1af9ec1e 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -1,6 +1,12 @@
{
"version": "1.0.0",
"messages": {
+ "-1993693214": {
+ "message": "Letterbox Task Changed: #%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/letterbox\/LetterboxTaskListener.java"
+ },
"-1683614271": {
"message": "Existing task: id=%d component=%s",
"level": "VERBOSE",
@@ -67,18 +73,18 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
- "-848099324": {
- "message": "Letterbox Task Appeared: #%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/LetterboxTaskListener.java"
- },
"-842742255": {
"message": "%s onTaskAppeared unknown taskId=%d winMode=%d",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java"
},
+ "-742394458": {
+ "message": "pair task1=%d task2=%d in AppPair=%s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPair.java"
+ },
"-710770147": {
"message": "Add target: %s",
"level": "VERBOSE",
@@ -91,6 +97,18 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/splitscreen\/SplitScreenTaskListener.java"
},
+ "-342975160": {
+ "message": "Letterbox Task Vanished: #%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/letterbox\/LetterboxTaskListener.java"
+ },
+ "-234284913": {
+ "message": "unpair taskId=%d pair=%s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPairsController.java"
+ },
"-191422040": {
"message": "Transition animations finished, notifying core %s",
"level": "VERBOSE",
@@ -139,17 +157,29 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "900599280": {
+ "message": "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
+ "level": "ERROR",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPair.java"
+ },
+ "950299522": {
+ "message": "taskId %d isn't isn't in an app-pair.",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPairsController.java"
+ },
"980952660": {
"message": "Task root back pressed taskId=%d",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
- "1104702476": {
- "message": "Letterbox Task Changed: #%d",
+ "1079041527": {
+ "message": "incrementPool size=%d",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/LetterboxTaskListener.java"
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPairsPool.java"
},
"1184615936": {
"message": "Set drop target window visibility: displayId=%d visibility=%d",
@@ -157,12 +187,6 @@
"group": "WM_SHELL_DRAG_AND_DROP",
"at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
},
- "1218010718": {
- "message": "Letterbox Task Vanished: #%d",
- "level": "VERBOSE",
- "group": "WM_SHELL_TASK_ORG",
- "at": "com\/android\/wm\/shell\/LetterboxTaskListener.java"
- },
"1481772149": {
"message": "Current target: %s",
"level": "VERBOSE",
@@ -175,12 +199,30 @@
"group": "WM_SHELL_DRAG_AND_DROP",
"at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
},
+ "1885882094": {
+ "message": "Letterbox Task Appeared: #%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/letterbox\/LetterboxTaskListener.java"
+ },
+ "1891981945": {
+ "message": "release entry.taskId=%s listener=%s size=%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPairsPool.java"
+ },
"1990759023": {
"message": "addListenerForType types=%s listener=%s",
"level": "VERBOSE",
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "2006473416": {
+ "message": "acquire entry.taskId=%s listener=%s size=%d",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/apppairs\/AppPairsPool.java"
+ },
"2057038970": {
"message": "Display changed: %d",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
new file mode 100644
index 000000000000..ea634cfa907c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_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>
+ <string name="pip_play" msgid="3496151081459417097">"Speel"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Laat wag"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Program steun nie begin op sekondêre skerms nie."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Skermverdeler"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Volskerm links"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Links 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Links 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Links 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Volskerm regs"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Volskerm bo"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Bo 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bo 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Bo 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Volskerm onder"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Gebruik eenhandmodus"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Swiep van die onderkant van die skerm af op of tik enige plek bo die program om uit te gaan"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Begin eenhandmodus"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Verlaat eenhandmodus"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Instellings vir <xliff:g id="APP_NAME">%1$s</xliff:g>-borrels"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Oorloop"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Voeg terug op stapel"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> vanaf <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> van <xliff:g id="APP_NAME">%2$s</xliff:g> en <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> meer af"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Beweeg na links bo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Beweeg na regs bo"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Beweeg na links onder"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Beweeg na regs onder"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-instellings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Maak borrel toe"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Moenie dat gesprek \'n borrel word nie"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Klets met borrels"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nuwe gesprekke verskyn as swerwende ikone, of borrels Tik op borrel om dit oop te maak. Sleep om dit te skuif."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Beheer borrels enige tyd"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Bestuur om borrels vanaf hierdie program af te skakel"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Het dit"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen onlangse borrels nie"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Onlangse borrels en borrels wat toegemaak is, sal hier verskyn"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-af/strings_tv.xml b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
new file mode 100644
index 000000000000..6ce588034f9e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-af/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Beeld-in-beeld"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Titellose program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Maak PIP toe"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Volskerm"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
new file mode 100644
index 000000000000..e4628d7b5278
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"ዝጋ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ዘርጋ"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ቅንብሮች"</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_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="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>
+ <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_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_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="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> አረፋዎች"</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="NOTIFICATION_TITLE">%1$s</xliff:g> ከ<xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ከ <xliff:g id="APP_NAME">%2$s</xliff:g> እና <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ተጨማሪ"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ወደ ላይኛው ግራ አንቀሳቅስ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"የግርጌውን ግራ አንቀሳቅስ"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ታችኛውን ቀኝ ያንቀሳቅሱ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"የ<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ቅንብሮች"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"አረፋን አሰናብት"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ውይይቶችን በአረፋ አታሳይ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"አረፋዎችን በመጠቀም ይወያዩ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"አዲስ ውይይቶች እንደ ተንሳፋፊ አዶዎች ወይም አረፋዎች ሆነው ይታያሉ። አረፋን ለመክፈት መታ ያድርጉ። ለመውሰድ ይጎትቱት።"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"በማንኛውም ጊዜ አረፋዎችን ይቆጣጠሩ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"የዚህ መተግበሪያ አረፋዎችን ለማጥፋት አቀናብርን መታ ያድርጉ"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ገባኝ"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ምንም የቅርብ ጊዜ አረፋዎች የሉም"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"የቅርብ ጊዜ አረፋዎች እና የተሰናበቱ አረፋዎች እዚህ ብቅ ይላሉ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings_tv.xml b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
new file mode 100644
index 000000000000..fcb87c5682e3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-am/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(ርዕስ የሌለው ፕሮግራም)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIPን ዝጋ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ሙሉ ማያ ገጽ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
new file mode 100644
index 000000000000..6c7faded7dad
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"إغلاق"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"توسيع"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"الإعدادات"</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_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="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>
+ <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_70" msgid="8859845045360659250">"ضبط حجم النافذة اليسرى ليكون ٧٠%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ضبط حجم النافذة اليسرى ليكون ٥٠%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ضبط حجم النافذة اليسرى ليكون ٣٠%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"عرض النافذة اليمنى بملء الشاشة"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"عرض النافذة العلوية بملء الشاشة"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"ضبط حجم النافذة العلوية ليكون ٧٠%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"ضبط حجم النافذة العلوية ليكون ٥٠%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"ضبط حجم النافذة العلوية ليكون ٣٠%"</string>
+ <string name="accessibility_action_divider_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>"</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="NOTIFICATION_TITLE">%1$s</xliff:g> من <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> من <xliff:g id="APP_NAME">%2$s</xliff:g> و<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> أيضًا"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"نقل إلى أعلى يمين الشاشة"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"الانتقال إلى أعلى اليسار"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نقل إلى أسفل يمين الشاشة"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نقل إلى أسفل اليسار"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"إعدادات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"إغلاق فقاعة المحادثة"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"عدم عرض المحادثة كفقاعة محادثة"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"الدردشة باستخدام فقاعات المحادثات"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"تظهر المحادثات الجديدة كرموز عائمة أو كفقاعات. انقر لفتح فقاعة المحادثة، واسحبها لتحريكها."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"التحكّم في فقاعات المحادثات في أي وقت"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"انقر على \"إدارة\" لإيقاف فقاعات المحادثات من هذا التطبيق."</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"حسنًا"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ليس هناك فقاعات محادثات"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ستظهر هنا أحدث فقاعات المحادثات وفقاعات المحادثات التي تم إغلاقها."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings_tv.xml b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
new file mode 100644
index 000000000000..4eef29e2ed12
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ar/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(ليس هناك عنوان للبرنامج)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"‏إغلاق PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ملء الشاشة"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
new file mode 100644
index 000000000000..47294c438729
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"বন্ধ কৰক"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"বিস্তাৰ কৰক"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ছেটিংসমূহ"</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_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="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>
+ <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_70" msgid="8859845045360659250">"বাওঁফালৰ স্ক্ৰীণখন ৭০% কৰক"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"বাওঁফালৰ স্ক্ৰীণখন ৫০% কৰক"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"বাওঁফালৰ স্ক্ৰীণখন ৩০% কৰক"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"সোঁফালৰ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"শীৰ্ষ স্ক্ৰীণখন সম্পূৰ্ণ স্ক্ৰীণ কৰক"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"শীর্ষ স্ক্ৰীণখন ৭০% কৰক"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"শীর্ষ স্ক্ৰীণখন ৫০% কৰক"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"শীর্ষ স্ক্ৰীণখন ৩০% কৰক"</string>
+ <string name="accessibility_action_divider_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="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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> আৰু<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>টাৰ পৰা <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"শীৰ্ষৰ বাওঁফালে নিয়ক"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
+ <string name="bubble_accessibility_action_move_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="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>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"নতুন বাৰ্তালাপ উপঙি থকা চিহ্নসমূহ অথবা bubbles হিচাপে প্ৰদর্শিত হয়। Bubbles খুলিবলৈ টিপক। এইটো স্থানান্তৰ কৰিবলৈ টানি নিয়ক।"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"যিকোনো সময়তে bubbles নিয়ন্ত্ৰণ কৰক"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"এই এপ্‌টোৰ পৰা bubbles অফ কৰিবলৈ পৰিচালনা কৰকত টিপক"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"বুজি পালোঁ"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"কোনো শেহতীয়া bubbles নাই"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"শেহতীয়া bubbles আৰু অগ্ৰাহ্য কৰা bubbles ইয়াত প্ৰদর্শিত হ\'ব"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings_tv.xml b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
new file mode 100644
index 000000000000..6c223f45d9b3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-as/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(শিৰোনামবিহীন কাৰ্যক্ৰম)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"পিপ বন্ধ কৰক"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"সম্পূৰ্ণ স্ক্ৰীণ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
new file mode 100644
index 000000000000..923ff79e0627
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_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>
+ <string name="pip_play" msgid="3496151081459417097">"Oxudun"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Fasilə verin"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Tətbiq ikinci ekranda başlamağı dəstəkləmir."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Bölünmüş ekran ayırıcısı"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Sol tam ekran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Sol 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Sol 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Sol 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Sağ tam ekran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Yuxarı tam ekran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Yuxarı 70%"</string>
+ <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_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="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>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> tətbiqindən <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> tətbiqindən <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> və daha <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> qabarcıq"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Yuxarıya sola köçürün"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuxarıya sağa köçürün"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Aşağıya sola köçürün"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Aşağıya sağa köçürün"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Yumrucuğu ləğv edin"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Söhbəti yumrucuqda göstərmə"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Yumrucuqlardan istifadə edərək söhbət edin"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Yeni söhbətlər üzən nişanlar və ya yumrucuqlar kimi görünür. Yumrucuğu açmaq üçün toxunun. Hərəkət etdirmək üçün sürüşdürün."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Yumrucuqları istənilən vaxt idarə edin"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Bu tətbiqdə yumrucuqları deaktiv etmək üçün \"İdarə edin\" seçiminə toxunun"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Anladım"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Yumrucuqlar yoxdur"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son yumrucuqlar və buraxılmış yumrucuqlar burada görünəcək"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings_tv.xml b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
new file mode 100644
index 000000000000..c9f1acbef31b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-az/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Şəkil-içində-Şəkil"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıqsız proqram)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP bağlayın"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</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
new file mode 100644
index 000000000000..02e609cd5c9b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_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>
+ <string name="pip_play" msgid="3496151081459417097">"Pusti"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pauziraj"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Razdelnik podeljenog ekrana"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Režim celog ekrana za levi ekran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Levi ekran 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Levi ekran 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Levi ekran 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Režim celog ekrana za donji ekran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Režim celog ekrana za gornji ekran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gornji ekran 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gornji ekran 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gornji ekran 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Režim celog ekrana za donji ekran"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Korišćenje režima jednom rukom"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Da biste izašli, prevucite nagore od dna ekrana ili dodirnite bilo gde iznad aplikacije"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Pokrenite režim jednom rukom"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Izađite iz režima jednom rukom"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Podešavanja za <xliff:g id="APP_NAME">%1$s</xliff:g> oblačiće"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Preklapanje"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodaj ponovo u grupu"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g> i još <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Premesti gore levo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premesti gore desno"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premesti dole levo"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premesti dole desno"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Podešavanja za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne koristi oblačiće za konverzaciju"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Ćaskajte u oblačićima"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nove konverzacije se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da biste otvorili oblačić. Prevucite da biste ga premestili."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrolišite oblačiće u bilo kom trenutku"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Dodirnite Upravljajte da biste isključili oblačiće iz ove aplikacije"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Važi"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovde se prikazuju nedavni i odbačeni oblačići"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+</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
new file mode 100644
index 000000000000..6fbc91bbec60
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_fullscreen" msgid="7278047353591302554">"Ceo ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
new file mode 100644
index 000000000000..ccea3180f64e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Закрыць"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Разгарнуць"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Налады"</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_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="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>
+ <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_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_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="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>\""</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="NOTIFICATION_TITLE">%1$s</xliff:g> ад праграмы \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ад праграмы \"<xliff:g id="APP_NAME">%2$s</xliff:g>\" і яшчэ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Перамясціць лявей і вышэй"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перамясціце правей і вышэй"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перамясціць лявей і ніжэй"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перамясціць правей і ніжэй"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Налады \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Адхіліць апавяшчэнне"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не паказваць размову ў выглядзе ўсплывальных апавяшчэнняў"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Усплывальныя апавяшчэнні"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новыя размовы будуць паказвацца як рухомыя значкі ці ўсплывальныя апавяшчэнні. Націсніце, каб адкрыць усплывальнае апавяшчэнне. Перацягніце яго, каб перамясціць."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Кіруйце ўсплывальнымі апавяшчэннямі ў любы час"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Каб выключыць усплывальныя апавяшчэнні з гэтай праграмы, націсніце \"Кіраваць\""</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Зразумела"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма нядаўніх усплывальных апавяшчэнняў"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Нядаўнія і адхіленыя ўсплывальныя апавяшчэнні будуць паказаны тут"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings_tv.xml b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
new file mode 100644
index 000000000000..973ae8ede5b3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-be/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(Праграма без назвы)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Закрыць PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Ва ўвесь экран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
new file mode 100644
index 000000000000..d29660b9c24d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Затваряне"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Разгъване"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Настройки"</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_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="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>
+ <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_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_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="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>"</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="NOTIFICATION_TITLE">%1$s</xliff:g> от <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ от<xliff:g id="APP_NAME">%2$s</xliff:g> и още <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Преместване горе вляво"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Преместване горе вдясно"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Преместване долу вляво"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Преместване долу вдясно"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Настройки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Отхвърляне на балончетата"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Без балончета за разговора"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Чат с балончета"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новите разговори се показват като плаващи икони, или балончета. Докоснете балонче, за да го отворите, или го плъзнете, за да го преместите."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Управление на балончетата по всяко време"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Докоснете „Управление“, за да изключите балончетата от това приложение"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Разбрах"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма скорошни балончета"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Скорошните и отхвърлените балончета ще се показват тук"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings_tv.xml b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
new file mode 100644
index 000000000000..f4fad601179f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bg/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без заглавие)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Затваряне на PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Цял екран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
new file mode 100644
index 000000000000..84bcaf907d91
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"বন্ধ করুন"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"বড় করুন"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"সেটিংস"</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_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="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>
+ <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_70" msgid="8859845045360659250">"৭০% বাকি আছে"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"৫০% বাকি আছে"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"৩০% বাকি আছে"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ডান দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"উপর দিকের অংশ নিয়ে পূর্ণ স্ক্রিন"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"শীর্ষ ৭০%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"শীর্ষ ৫০%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"শীর্ষ ৩০%"</string>
+ <string name="accessibility_action_divider_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> বাবলের জন্য সেটিংস"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> অ্যাপ এবং আরও <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>টি থেকে <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"উপরে বাঁদিকে সরান"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"উপরে ডানদিকে সরান"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"বাবল খারিজ করুন"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"কথোপকথন বাবল হিসেবে দেখাবে না"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"বাবল ব্যবহার করে চ্যাট করুন"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"নতুন কথোপকথন ভেসে থাকা আইকন বা বাবল হিসেবে দেখানো হয়। বাবল খুলতে ট্যাপ করুন। সেটি সরাতে ধরে টেনে আনুন।"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"যেকোনও সময় বাবল নিয়ন্ত্রণ করুন"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"এই অ্যাপ থেকে বাবল বন্ধ করতে \'ম্যানেজ করুন\' বিকল্প ট্যাপ করুন"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"বুঝেছি"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"কোনও সাম্প্রতিক বাবল নেই"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"সাম্প্রতিক ও বাতিল করা বাবল এখানে দেখা যাবে"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings_tv.xml b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
new file mode 100644
index 000000000000..0eb83a0276e6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bn/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(শিরোনামহীন প্রোগ্রাম)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP বন্ধ করুন"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"পূর্ণ স্ক্রিন"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
new file mode 100644
index 000000000000..85e08d7ca555
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Zatvori"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Proširi"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Postavke"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Reproduciraj"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pauziraj"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim ekranima."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Razdjelnik ekrana"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lijevo cijeli ekran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Lijevo 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Lijevo 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Lijevo 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Desno cijeli ekran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Gore cijeli ekran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gore 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gore 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gore 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Donji ekran kao cijeli ekran"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Korištenje načina rada jednom rukom"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Da izađete, prevucite s dna ekrana prema gore ili dodirnite bilo gdje iznad aplikacije"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Započinjanje načina rada jednom rukom"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Izlaz iz načina rada jednom rukom"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Postavke za oblačiće aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Preklapanje"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodaj nazad u grupu"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> od aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"Obavještenje <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g> i još <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Pomjeri gore lijevo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pomjerite gore desno"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pomjeri dolje lijevo"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pomjerite dolje desno"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke aplikacije <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nemoj prikazivati razgovor u oblačićima"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatajte koristeći oblačiće"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi razgovori se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da otvorite oblačić. Prevucite da ga premjestite."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Upravljajte oblačićima u svakom trenutku"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Dodirnite Upravljaj da isključite oblačiće iz ove aplikacije"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Razumijem"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nedavni i odbačeni oblačići će se pojaviti ovdje"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings_tv.xml b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
new file mode 100644
index 000000000000..8e301b0a8f4d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-bs/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_fullscreen" msgid="7278047353591302554">"Cijeli ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
new file mode 100644
index 000000000000..a80b7fbec09a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Tanca"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Desplega"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Configuració"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Reprodueix"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Posa en pausa"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'aplicació no es pot obrir en pantalles secundàries."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalles"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla esquerra completa"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Pantalla esquerra al 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Pantalla esquerra al 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Pantalla esquerra al 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla dreta completa"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla superior completa"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Pantalla superior al 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Pantalla superior al 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Pantalla superior al 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"S\'està utilitzant el mode d\'una mà"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Per sortir, llisca cap amunt des de la part inferior de la pantalla o toca qualsevol lloc a sobre de l\'aplicació"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Inicia el mode d\'una mà"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Surt del mode d\'una mà"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configuració de les bombolles: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú addicional"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Torna a afegir a la pila"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de: <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>) i <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> més"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mou a dalt a l\'esquerra"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mou a dalt a la dreta"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mou a baix a l\'esquerra"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mou a baix a la dreta"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuració de l\'aplicació <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora la bombolla"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostris la conversa com a bombolla"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Xateja amb bombolles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les converses noves es mostren com a icones flotants o bombolles. Toca per obrir una bombolla. Arrossega-la per moure-la."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla les bombolles en qualsevol moment"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toca Gestiona per desactivar les bombolles d\'aquesta aplicació"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entesos"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hi ha bombolles recents"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bombolles recents i les ignorades es mostraran aquí"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings_tv.xml b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
new file mode 100644
index 000000000000..b80fc41402dd
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ca/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Pantalla en pantalla"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sense títol)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Tanca PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
new file mode 100644
index 000000000000..e8257bc8ee92
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Zavřít"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Rozbalit"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Nastavení"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Přehrát"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pozastavit"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikace nepodporuje spuštění na sekundárních displejích."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Čára rozdělující obrazovku"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Levá část na celou obrazovku"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % vlevo"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % vlevo"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % vlevo"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pravá část na celou obrazovku"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Horní část na celou obrazovku"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % nahoře"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % nahoře"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % nahoře"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Dolní část na celou obrazovku"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Používání režimu jedné ruky"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Režim ukončíte, když přejedete prstem z dolní části obrazovky nahoru nebo klepnete kamkoli nad aplikaci"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Spustit režim jedné ruky"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Ukončit režim jedné ruky"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Nastavení bublin aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Rozbalovací nabídka"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Přidat zpět do sady"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"Oznámení <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikace <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikace <xliff:g id="APP_NAME">%2$s</xliff:g> a dalších (<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>)"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Přesunout vlevo nahoru"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Přesunout vpravo nahoru"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Přesunout vlevo dolů"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Přesunout vpravo dolů"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavení <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavřít bublinu"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovat konverzaci v bublinách"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatujte pomocí bublin"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nové konverzace se zobrazují jako plovoucí ikony, neboli bubliny. Klepnutím bublinu otevřete. Přetažením ji posunete."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Nastavení bublin můžete kdykoli upravit"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Bubliny pro tuto aplikaci můžete vypnout klepnutím na Spravovat"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žádné nedávné bubliny"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Zde se budou zobrazovat nedávné bubliny a zavřené bubliny"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings_tv.xml b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
new file mode 100644
index 000000000000..56abcbe473fb
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-cs/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Obraz v obraze"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Bez názvu)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Ukončit obraz v obraze (PIP)"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
new file mode 100644
index 000000000000..17f8286e8069
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Luk"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Udvid"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Indstillinger"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Afspil"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Sæt på pause"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke åbnes på sekundære skærme."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Adskiller til opdelt skærm"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vis venstre del i fuld skærm"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Venstre 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Venstre 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Venstre 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Vis højre del i fuld skærm"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Vis øverste del i fuld skærm"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Øverste 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Øverste 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Øverste 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Vis nederste del i fuld skærm"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Brug af enhåndstilstand"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Du kan afslutte ved at stryge opad fra bunden af skærmen eller trykke et vilkårligt sted over appen"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start enhåndstilstand"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Afslut enhåndstilstand"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Indstillinger for <xliff:g id="APP_NAME">%1$s</xliff:g>-bobler"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overløb"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Føj til stak igen"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g> og <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> andre"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Flyt op til venstre"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flyt op til højre"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flyt ned til venstre"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flyt ned til højre"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Indstillinger for <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Afvis boble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Vis ikke samtaler i bobler"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat ved hjælp af bobler"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nye samtaler vises som svævende ikoner eller bobler. Tryk for at åbne boblen. Træk for at flytte den."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Styr bobler når som helst"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tryk på Administrer for at deaktivere bobler fra denne app"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen seneste bobler"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nye bobler og afviste bobler vises her"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings_tv.xml b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
new file mode 100644
index 000000000000..fdb6b783399e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-da/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Integreret billede"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uden titel)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Luk integreret billede"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Fuld skærm"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
new file mode 100644
index 000000000000..4979a4015525
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Schließen"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Maximieren"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Einstellungen"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Wiedergeben"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausieren"</string>
+ <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>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Die App unterstützt den Start auf sekundären Displays nicht."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Bildschirmteiler"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vollbild links"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % links"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % links"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % links"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Vollbild rechts"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Vollbild oben"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % oben"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % oben"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % oben"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Vollbild unten"</string>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Einstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g>-Bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Mehr anzeigen"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Wieder dem Stapel hinzufügen"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> von <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aus <xliff:g id="APP_NAME">%2$s</xliff:g> und <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> weiteren"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Nach oben links verschieben"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Nach rechts oben verschieben"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Nach unten links verschieben"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Nach unten rechts verschieben"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Einstellungen für <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string>
+ <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_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>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings_tv.xml b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
new file mode 100644
index 000000000000..0432f1c353e4
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-de/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
new file mode 100644
index 000000000000..cc329e8f3274
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Κλείσιμο"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Ανάπτυξη"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ρυθμίσεις"</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>
+ <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="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>
+ <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_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_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="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>"</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="NOTIFICATION_TITLE">%1$s</xliff:g> από <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> από την εφαρμογή <xliff:g id="APP_NAME">%2$s</xliff:g> και <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ακόμη"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Μετακίνηση επάνω αριστερά"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Μετακίνηση επάνω δεξιά"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Μετακίνηση κάτω αριστερά"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Μετακίνηση κάτω δεξιά"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Ρυθμίσεις <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Παράβλ. για συννεφ."</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Να μην γίνει προβολή της συζήτησης σε συννεφάκια."</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Συζητήστε χρησιμοποιώντας συννεφάκια."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Οι νέες συζητήσεις εμφανίζονται ως κινούμενα εικονίδια ή συννεφάκια. Πατήστε για να ανοίξετε το συννεφάκι. Σύρετε για να το μετακινήσετε."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Ελέγξτε τα συννεφάκια ανά πάσα στιγμή."</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Πατήστε Διαχείριση για να απενεργοποιήσετε τα συννεφάκια από αυτήν την εφαρμογή."</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Το κατάλαβα"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Δεν υπάρχουν πρόσφατα συννεφάκια"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Τα πρόσφατα συννεφάκια και τα συννεφάκια που παραβλέψατε θα εμφανίζονται εδώ."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings_tv.xml b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
new file mode 100644
index 000000000000..880ea37e6bf7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-el/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Δεν υπάρχει τίτλος προγράμματος)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Κλείσιμο PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Πλήρης οθόνη"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..90c71c0e11ea
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Close"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pause"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Using one-handed mode"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start one-handed mode"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Exit one-handed mode"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Settings for <xliff:g id="APP_NAME">%1$s</xliff:g> bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Add back to stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> more"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Move top left"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles at any time"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tap Manage to turn off bubbles from this app"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+</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
new file mode 100644
index 000000000000..e3f08c8cc76f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..90c71c0e11ea
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Close"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pause"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Using one-handed mode"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start one-handed mode"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Exit one-handed mode"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Settings for <xliff:g id="APP_NAME">%1$s</xliff:g> bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Add back to stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> more"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Move top left"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles at any time"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tap Manage to turn off bubbles from this app"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+</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
new file mode 100644
index 000000000000..e3f08c8cc76f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..90c71c0e11ea
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Close"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pause"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Using one-handed mode"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start one-handed mode"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Exit one-handed mode"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Settings for <xliff:g id="APP_NAME">%1$s</xliff:g> bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Add back to stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> more"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Move top left"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles at any time"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tap Manage to turn off bubbles from this app"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+</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
new file mode 100644
index 000000000000..e3f08c8cc76f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..90c71c0e11ea
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Close"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pause"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App does not support launch on secondary displays."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Split screen divider"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Left full screen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Left 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Left 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Left 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Right full screen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Top full screen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Top 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Top 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Top 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Bottom full screen"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Using one-handed mode"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start one-handed mode"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Exit one-handed mode"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Settings for <xliff:g id="APP_NAME">%1$s</xliff:g> bubbles"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Add back to stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g> and <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> more"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Move top left"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Control bubbles at any time"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tap Manage to turn off bubbles from this app"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+</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
new file mode 100644
index 000000000000..e3f08c8cc76f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..d8b5b40035f7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎Close‎‏‎‎‏‎"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎Expand‎‏‎‎‏‎"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‎Settings‎‏‎‎‏‎"</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>
+ <string name="pip_play" msgid="3496151081459417097">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‏‎‎‏‎Play‎‏‎‎‏‎"</string>
+ <string name="pip_pause" msgid="690688849510295232">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‎Pause‎‏‎‎‏‎"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‎App does not support launch on secondary displays.‎‏‎‎‏‎"</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎Split-screen divider‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎Left full screen‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎Left 70%‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎Left 50%‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‏‏‎Left 30%‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‏‏‎Right full screen‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎Top full screen‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎Top 70%‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‎‎Top 50%‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎Top 30%‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎Bottom full screen‎‏‎‎‏‎"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎‎Using one-handed mode‎‏‎‎‏‎"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‏‎‏‏‎To exit, swipe up from the bottom of the screen or tap anywhere above the app‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‎‎‏‎‎Start one-handed mode‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‎‎Exit one-handed mode‎‏‎‎‏‎"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‎‎‎‎Settings for ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ bubbles‎‏‎‎‏‎"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎Overflow‎‏‎‎‏‎"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‏‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‎Add back to stack‎‏‎‎‏‎"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ from ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ from ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>‎‏‎‎‏‏‏‎ more‎‏‎‎‏‎"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‎Move top left‎‏‎‎‏‎"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‏‎Move top right‎‏‎‎‏‎"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‎Move bottom left‎‏‎‎‏‎"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎Move bottom right‎‏‎‎‏‎"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ settings‎‏‎‎‏‎"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎Dismiss bubble‎‏‎‎‏‎"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎Don’t bubble conversation‎‏‎‎‏‎"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎Chat using bubbles‎‏‎‎‏‎"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‎New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it.‎‏‎‎‏‎"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎Control bubbles anytime‎‏‎‎‏‎"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‎Tap Manage to turn off bubbles from this app‎‏‎‎‏‎"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎Got it‎‏‎‎‏‎"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‎No recent bubbles‎‏‎‎‏‎"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎Recent bubbles and dismissed bubbles will appear here‎‏‎‎‏‎"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‎‎Bubble‎‏‎‎‏‎"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎Manage‎‏‎‎‏‎"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎Bubble dismissed.‎‏‎‎‏‎"</string>
+</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
new file mode 100644
index 000000000000..3f9ef0ea2816
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‏‎Picture-in-Picture‎‏‎‎‏‎"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..7244b1a1bcf5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Cerrar"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Configuración"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Reproducir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La app no puede iniciarse en pantallas secundarias."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalla dividida"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla izquierda completa"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Izquierda: 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Izquierda: 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Izquierda: 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla derecha completa"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla superior completa"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Superior: 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior: 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior: 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Cómo usar el modo de una mano"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o presiona cualquier parte arriba de la app"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar el modo de una mano"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del modo de una mano"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configuración para burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú ampliado"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a agregar a la pila"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> y <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> más"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Ubicar arriba a la izquierda"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ubicar arriba a la derecha"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ubicar abajo a la izquierda"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string>
+ <string name="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_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>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Presiona Administrar para desactivar las burbujas de esta app"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las que se descartaron aparecerán aquí"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
+</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
new file mode 100644
index 000000000000..5d5954a19761
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Pantalla en pantalla"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sin título de programa)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
new file mode 100644
index 000000000000..65e75bde573d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Cerrar"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Mostrar"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ajustes"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Reproducir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"La aplicación no se puede abrir en pantallas secundarias."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Dividir la pantalla"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla izquierda completa"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Izquierda 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Izquierda 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Izquierda 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla derecha completa"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla superior completa"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Superior 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utilizar el modo una mano"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo una mano"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del modo una mano"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ajustes de las burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú adicional"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a añadir a la pila"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> y <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> más"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover arriba a la izquierda"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover arriba a la derecha"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover abajo a la izquierda."</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover abajo a la derecha"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Ajustes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string>
+ <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_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>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las cerradas aparecerán aquí"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings_tv.xml b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
new file mode 100644
index 000000000000..d31b9b45cae3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-es/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Imagen en imagen"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sin título)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Cerrar PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
new file mode 100644
index 000000000000..0ccfcfee85d6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Sule"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Laiendamine"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Seaded"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Esita"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Peata"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Rakendus ei toeta teisestel ekraanidel käivitamist."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Ekraanijagaja"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vasak täisekraan"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vasak: 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vasak: 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vasak: 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Parem täisekraan"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ülemine täisekraan"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Ülemine: 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Ülemine: 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Ülemine: 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Alumine täisekraan"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Ühekäerežiimi kasutamine"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Väljumiseks pühkige ekraani alaosast üles või puudutage rakenduse kohal olevat ala"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Ühekäerežiimi käivitamine"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Ühekäerežiimist väljumine"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> mullide seaded"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Ületäide"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Lisa tagasi virna"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> rakendusest <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> rakenduselt <xliff:g id="APP_NAME">%2$s</xliff:g> ja veel <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Teisalda üles vasakule"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Teisalda üles paremale"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Teisalda alla vasakule"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Teisalda alla paremale"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Rakenduse <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> seaded"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Sule mull"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ära kuva vestlust mullina"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Vestelge mullide abil"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Uued vestlused kuvatakse hõljuvate ikoonidena ehk mullidena. Puudutage mulli avamiseks. Lohistage mulli, et seda liigutada."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Juhtige mulle igal ajal"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Selle rakenduse puhul mullide väljalülitamiseks puudutage valikut Halda"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Selge"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hiljutisi mulle pole"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Siin kuvatakse hiljutised ja suletud mullid."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings_tv.xml b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
new file mode 100644
index 000000000000..bc7a6adafc03
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-et/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Pilt pildis"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programmi pealkiri puudub)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Sule PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Täisekraan"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
new file mode 100644
index 000000000000..6682ea80cf42
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Itxi"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Zabaldu"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ezarpenak"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Erreproduzitu"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausatu"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Saltatu hurrengora"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Saltatu aurrekora"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Aldatu tamaina"</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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikazioa ezin da abiarazi bigarren mailako pantailatan."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Pantaila-zatitzailea"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ezarri ezkerraldea pantaila osoan"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Ezarri ezkerraldea % 70en"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ezarri ezkerraldea % 50en"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ezarri ezkerraldea % 30en"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Ezarri eskuinaldea pantaila osoan"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ezarri goialdea pantaila osoan"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Ezarri goialdea % 70en"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Ezarri goialdea % 50en"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Ezarri goialdea % 30en"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ezarri behealdea pantaila osoan"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Esku bakarreko modua erabiltzea"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Irteteko, pasatu hatza pantailaren behealdetik gora edo sakatu aplikazioaren gainaldea"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Abiarazi esku bakarreko modua"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Irten esku bakarreko modutik"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren ezarpenen burbuilak"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Gainezkatzea"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Gehitu berriro errenkadan"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> aplikazioaren \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" jakinarazpena, eta beste <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Eraman goialdera, ezkerretara"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Eraman goialdera, eskuinetara"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Eraman behealdera, ezkerretara"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Eraman behealdera, eskuinetara"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aplikazioaren ezarpenak"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Baztertu burbuila"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ez erakutsi elkarrizketak burbuila gisa"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Txateatu burbuilen bidez"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Elkarrizketa berriak ikono gainerakor edo burbuila gisa agertzen dira. Sakatu burbuila irekitzeko. Arrasta ezazu mugitzeko."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrolatu burbuilak edonoiz"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Aplikazioaren burbuilak desaktibatzeko, sakatu Kudeatu"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ados"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ez dago azkenaldiko burbuilarik"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Azken burbuilak eta baztertutakoak agertuko dira hemen"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings_tv.xml b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
new file mode 100644
index 000000000000..cf5f98883082
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-eu/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Pantaila txiki gainjarria"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa izengabea)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Itxi PIPa"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantaila osoa"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
new file mode 100644
index 000000000000..a41811d53357
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"بستن"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"بزرگ کردن"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"تنظیمات"</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_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="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>
+ <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_70" msgid="8859845045360659250">"٪۷۰ چپ"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"٪۵۰ چپ"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"٪۳۰ چپ"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"تمام‌صفحه راست"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"تمام‌صفحه بالا"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"٪۷۰ بالا"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"٪۵۰ بالا"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"٪۳۰ بالا"</string>
+ <string name="accessibility_action_divider_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>"</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="NOTIFICATION_TITLE">%1$s</xliff:g> از <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> از <xliff:g id="APP_NAME">%2$s</xliff:g> و <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> مورد بیشتر"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"انتقال به بالا سمت راست"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"انتقال به بالا سمت چپ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"انتقال به پایین سمت راست"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"انتقال به پایین سمت چپ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"تنظیمات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"رد کردن حبابک"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"مکالمه در حباب نشان داده نشود"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابک‌ها"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمه‌های جدید به‌صورت نمادهای شناور یا حبابک‌ها نشان داده می‌شوند. برای باز کردن حبابک‌ها ضربه بزنید. برای جابه‌جایی، آن را بکشید."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کنترل حبابک‌ها در هرزمانی"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن «حبابک‌ها» از این برنامه، روی «مدیریت» ضربه بزنید"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجه‌ام"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌ها اخیر و حبابک‌ها ردشده اینجا ظاهر خواهند شد"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings_tv.xml b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
new file mode 100644
index 000000000000..5b815b4c7b86
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fa/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(برنامه بدون عنوان)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"‏بستن PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"تمام صفحه"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
new file mode 100644
index 000000000000..fcdc70fc9cda
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Sulje"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Laajenna"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Asetukset"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Toista"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Keskeytä"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Sovellus ei tue käynnistämistä toissijaisilla näytöillä."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Näytön jakaja"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vasen koko näytölle"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vasen 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vasen 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vasen 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Oikea koko näytölle"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Yläosa koko näytölle"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Yläosa 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yläosa 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Yläosa 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Alaosa koko näytölle"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Yhden käden moodin käyttö"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Poistu pyyhkäisemällä ylös näytön alareunasta tai napauttamalla sovelluksen yllä"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Käynnistä yhden käden moodi"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Poistu yhden käden moodista"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Kuplien asetukset: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Ylivuoto"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Lisää takaisin pinoon"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>) ja <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> muuta"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Siirrä vasempaan yläreunaan"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Siirrä oikeaan yläreunaan"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Siirrä vasempaan alareunaan"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Siirrä oikeaan alareunaan"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: asetukset"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ohita kupla"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Älä näytä kuplia keskusteluista"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chattaile kuplien avulla"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Uudet keskustelut näkyvät kelluvina kuvakkeina tai kuplina. Avaa kupla napauttamalla. Siirrä sitä vetämällä."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Muuta kuplien asetuksia milloin tahansa"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Valitse Ylläpidä, jos haluat poistaa kuplat käytöstä tästä sovelluksesta"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Okei"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ei viimeaikaisia kuplia"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viimeaikaiset ja äskettäin ohitetut kuplat näkyvät täällä"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings_tv.xml b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
new file mode 100644
index 000000000000..77ad6eef91e7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fi/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Kuva kuvassa"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nimetön)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Sulje PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Koko näyttö"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..ed822373e557
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_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>
+ <string name="pip_play" msgid="3496151081459417097">"Lire"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Interrompre"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Séparateur d\'écran partagé"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Plein écran à la gauche"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % à la gauche"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % à la gauche"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % à la gauche"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Plein écran à la droite"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Plein écran dans le haut"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % dans le haut"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % dans le haut"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % dans le haut"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Plein écran dans le bas"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utiliser le mode Une main"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pour quitter, balayez l\'écran du bas vers le haut, ou touchez n\'importe où sur l\'écran en haut de l\'application"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Démarrer le mode Une main"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Quitter le mode Une main"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Paramètres pour les bulles de l\'application <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu déroulant"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Replacer sur la pile"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> et <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> autres"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Déplacer dans coin sup. gauche"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer dans coin sup. droit"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer dans coin inf. gauche"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer dans coin inf. droit"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorer la bulle"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher les conversations dans des bulles"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Clavarder en utilisant des bulles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes (de bulles). Touchez une bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Paramètres des bulles"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toucher 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>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et les bulles ignorées s\'afficheront ici"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
+</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
new file mode 100644
index 000000000000..0ec7f40f0e9f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Incrustation d\'image"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Aucun programme de titre)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Fermer mode IDI"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Plein écran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
new file mode 100644
index 000000000000..ad98b85d5d5d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_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>
+ <string name="pip_play" msgid="3496151081459417097">"Lecture"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Suspendre"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'application ne peut pas être lancée sur des écrans secondaires."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Séparateur d\'écran partagé"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Écran de gauche en plein écran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Écran de gauche à 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Écran de gauche à 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Écran de gauche à 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Écran de droite en plein écran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Écran du haut en plein écran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Écran du haut à 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Écran du haut à 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Écran du haut à 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Écran du bas en plein écran"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utiliser le mode une main"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pour quitter, balayez l\'écran de bas en haut ou appuyez n\'importe où au-dessus de l\'application"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Démarrer le mode une main"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Quitter le mode une main"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Paramètres des bulles de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Dépassement"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Ajouter à nouveau l\'élément à la pile"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de l\'application <xliff:g id="APP_NAME">%2$s</xliff:g> et <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> autres"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Déplacer en haut à gauche"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer en haut à droite"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer en bas à gauche"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer en bas à droite"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Fermer la bulle"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string>
+ <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" 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>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et ignorées s\'afficheront ici"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings_tv.xml b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
new file mode 100644
index 000000000000..27fd155535b7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-fr/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
new file mode 100644
index 000000000000..529825e68151
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Pechar"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Despregar"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Configuración"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Reproducir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A aplicación non se pode iniciar en pantallas secundarias."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor de pantalla dividida"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Pantalla completa á esquerda"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70 % á esquerda"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50 % á esquerda"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30 % á esquerda"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pantalla completa á dereita"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Pantalla completa arriba"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70 % arriba"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % arriba"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % arriba"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla completa abaixo"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como se usa o modo dunha soa man?"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para saír, pasa o dedo cara arriba desde a parte inferior da pantalla ou toca calquera lugar da zona situada encima da aplicación"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo dunha soa man"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Saír do modo dunha soa man"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configuración das burbullas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Mostrar menú adicional"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Engadir de novo á pilla"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> e <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> máis"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover á parte super. esquerda"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover á parte superior dereita"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover á parte infer. esquerda"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover á parte inferior dereita"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar burbulla"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mostrar a conversa como burbulla"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatear usando burbullas"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"As conversas novas aparecen como iconas flotantes ou burbullas. Toca para abrir a burbulla e arrastra para movela."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla as burbullas"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Para desactivar as burbullas nesta aplicación, toca Xestionar"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Non hai burbullas recentes"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"As burbullas recentes e ignoradas aparecerán aquí."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings_tv.xml b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
new file mode 100644
index 000000000000..df96f6cb794d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-gl/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Pantalla superposta"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa sen título)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Pechar PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pantalla completa"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
new file mode 100644
index 000000000000..ee23e1e967ec
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"બંધ કરો"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"વિસ્તૃત કરો"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"સેટિંગ"</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_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="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>
+ <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_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_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="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> બબલ માટેનાં સેટિંગ"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> અને વધુ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> તરફથી <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ઉપર ડાબે ખસેડો"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ઉપર જમણે ખસેડો"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"બબલને છોડી દો"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"વાતચીતને બબલ કરશો નહીં"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"બબલનો ઉપયોગ કરીને ચેટ કરો"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"નવી વાતચીત ફ્લોટિંગ આઇકન અથવા બબલ જેવી દેખાશે. બબલને ખોલવા માટે ટૅપ કરો. તેને ખસેડવા માટે ખેંચો."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"બબલને કોઈપણ સમયે નિયંત્રિત કરો"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"આ ઍપમાંથી બબલને બંધ કરવા માટે મેનેજ કરો પર ટૅપ કરો"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"સમજાઈ ગયું"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"તાજેતરના કોઈ બબલ નથી"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"એકદમ નવા બબલ અને છોડી દીધેલા બબલ અહીં દેખાશે"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings_tv.xml b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
new file mode 100644
index 000000000000..3608f1d530c0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-gu/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(કોઈ ટાઇટલ પ્રોગ્રામ નથી)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP બંધ કરો"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"પૂર્ણ સ્ક્રીન"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
new file mode 100644
index 000000000000..34c1c85211f6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"बंद करें"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"विस्तार करें"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"सेटिंग"</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_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="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>
+ <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_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_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="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> बबल्स की सेटिंग"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> और <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> अन्य ऐप्लिकेशन से <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"सबसे ऊपर बाईं ओर ले जाएं"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सबसे ऊपर दाईं ओर ले जाएं"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारिज करें"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"बातचीत को बबल न करें"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"बबल्स का इस्तेमाल करके चैट करें"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"नई बातचीत फ़्लोटिंग आइकॉन या बबल्स की तरह दिखेंगी. बबल को खोलने के लिए टैप करें. इसे एक जगह से दूसरी जगह ले जाने के लिए खींचें और छोड़ें."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"जब चाहें, बबल्स को कंट्रोल करें"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"इस ऐप्लिकेशन पर बबल्स को बंद करने के लिए \'प्रबंधित करें\' पर टैप करें"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ठीक है"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के बबल्स मौजूद नहीं हैं"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"प्रबंधित करें"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings_tv.xml b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
new file mode 100644
index 000000000000..720bb6ca5e24
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hi/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(कोई शीर्षक कार्यक्रम नहीं)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP बंद करें"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"फ़ुल स्‍क्रीन"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
new file mode 100644
index 000000000000..32b21aadbb2f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Zatvori"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Proširivanje"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Postavke"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Reproduciraj"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pauziraj"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podržava pokretanje na sekundarnim zaslonima."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Razdjelnik podijeljenog zaslona"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lijevi zaslon u cijeli zaslon"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Lijevi zaslon na 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Lijevi zaslon na 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Lijevi zaslon na 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Desni zaslon u cijeli zaslon"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Gornji zaslon u cijeli zaslon"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gornji zaslon na 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gornji zaslon na 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gornji zaslon na 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Donji zaslon u cijeli zaslon"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Korištenje načina rada jednom rukom"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Za izlaz prijeđite prstom od dna zaslona prema gore ili dodirnite bio gdje iznad aplikacije"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Pokretanje načina rada jednom rukom"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Izlaz iz načina rada jednom rukom"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Postavke za oblačiće za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Dodatno"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodajte natrag u nizove"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g> i još <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Premjesti u gornji lijevi kut"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premjesti u gornji desni kut"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premjesti u donji lijevi kut"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premjestite u donji desni kut"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Zaustavi razgovor u oblačićima"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Oblačići u chatu"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi razgovori pojavljuju se kao pomične ikone ili oblačići. Dodirnite za otvaranje oblačića. Povucite da biste ga premjestili."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Upravljanje oblačićima u svakom trenutku"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Dodirnite Upravljanje da biste isključili oblačiće iz ove aplikacije"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Shvaćam"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovdje će se prikazivati nedavni i odbačeni oblačići"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings_tv.xml b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
new file mode 100644
index 000000000000..21f8cb63f470
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hr/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_fullscreen" msgid="7278047353591302554">"Cijeli zaslon"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
new file mode 100644
index 000000000000..123b127bd5a3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_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>
+ <string name="pip_play" msgid="3496151081459417097">"Lejátszás"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Szüneteltetés"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Az alkalmazást nem lehet másodlagos kijelzőn elindítani."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Elválasztó az osztott nézetben"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Bal oldali teljes képernyőre"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Bal oldali 70%-ra"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Bal oldali 50%-ra"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Bal oldali 30%-ra"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Jobb oldali teljes képernyőre"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Felső teljes képernyőre"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Felső 70%-ra"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Felső 50%-ra"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Felső 30%-ra"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Alsó teljes képernyőre"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Egykezes mód használata"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"A kilépéshez csúsztasson felfelé a képernyő aljáról, vagy koppintson az alkalmazás felett a képernyő bármelyik részére"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Egykezes mód indítása"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Kilépés az egykezes módból"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g>-buborékok beállításai"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"További elemeket tartalmazó menü"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Visszaküldés a verembe"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> a(z) <xliff:g id="APP_NAME">%2$s</xliff:g> alkalmazásból és <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> további"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Áthelyezés fel és balra"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Áthelyezés fel és jobbra"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Áthelyezés le és balra"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Áthelyezés le és jobbra"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> beállításai"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Buborék elvetése"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne jelenjen meg a beszélgetés buborékban"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Buborékokat használó csevegés"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Az új beszélgetések lebegő ikonként, vagyis buborékként jelennek meg. A buborék megnyitásához koppintson rá. Áthelyezéshez húzza a kívánt helyre."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Buborékok vezérlése bármikor"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"A Kezelés gombra koppintva kapcsolhatja ki az alkalmazásból származó buborékokat"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Értem"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nincsenek buborékok a közelmúltból"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"A legutóbbi és az elvetett buborékok itt jelennek majd meg"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings_tv.xml b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
new file mode 100644
index 000000000000..0010086bb0b5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hu/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Kép a képben"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Cím nélküli program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP bezárása"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Teljes képernyő"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
new file mode 100644
index 000000000000..b047cf131aa8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Փակել"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Ընդարձակել"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Կարգավորումներ"</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_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="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>
+ <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_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_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="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>-ի ամպիկների կարգավորումներ"</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="NOTIFICATION_TITLE">%1$s</xliff:g>՝ <xliff:g id="APP_NAME">%2$s</xliff:g>-ից"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>` <xliff:g id="APP_NAME">%2$s</xliff:g>-ից ու ևս <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ամպիկ"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Տեղափոխել վերև՝ ձախ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Տեղափոխել վերև՝ աջ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Տեղափոխել ներքև՝ ձախ"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Տեղափոխել ներքև՝ աջ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – կարգավորումներ"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Փակել ամպիկը"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Զրույցը չցուցադրել ամպիկի տեսքով"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Զրույցի ամպիկներ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Նոր զրույցները կհայտնվեն լողացող պատկերակների կամ ամպիկների տեսքով։ Հպեք՝ ամպիկը բացելու համար։ Քաշեք՝ այն տեղափոխելու համար։"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Ամպիկների կարգավորումներ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Հպեք «Կառավարել» կոճակին՝ այս հավելվածի ամպիկներն անջատելու համար։"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Եղավ"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ամպիկներ չկան"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Այստեղ կցուցադրվեն վերջերս օգտագործված և փակված ամպիկները, որոնք կկարողանաք հեշտությամբ վերաբացել"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings_tv.xml b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
new file mode 100644
index 000000000000..cb18762be48b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-hy/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(Առանց վերնագրի ծրագիր)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Փակել PIP-ն"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Լիէկրան"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
new file mode 100644
index 000000000000..a75cdb4b2b85
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Tutup"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Luaskan"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Setelan"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Putar"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Jeda"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikasi tidak mendukung peluncuran pada layar sekunder."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Pembagi layar terpisah"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Layar penuh di kiri"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kiri 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kiri 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kiri 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Layar penuh di kanan"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Layar penuh di atas"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Atas 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Atas 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Atas 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Layar penuh di bawah"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Menggunakan mode satu tangan"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Untuk keluar, geser layar dari bawah ke atas atau ketuk di mana saja di atas aplikasi"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Mulai mode satu tangan"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Keluar dari mode satu tangan"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Setelan untuk balon <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Tambahan"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Tambahkan kembali ke stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> dari <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> dari <xliff:g id="APP_NAME">%2$s</xliff:g> dan <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> lainnya"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Pindahkan ke kiri atas"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pindahkan ke kanan atas"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pindahkan ke kiri bawah"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pindahkan ke kanan bawah"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Setelan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Tutup balon"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan gunakan percakapan balon"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat dalam tampilan balon"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Percakapan baru muncul sebagai ikon mengambang, atau balon. Ketuk untuk membuka balon. Tarik untuk memindahkannya."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrol balon kapan saja"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Ketuk Kelola untuk menonaktifkan balon dari aplikasi ini"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Oke"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tidak ada balon baru-baru ini"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Balon yang baru dipakai dan balon yang telah ditutup akan muncul di sini"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings_tv.xml b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
new file mode 100644
index 000000000000..8f3a28764b00
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-in/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
new file mode 100644
index 000000000000..3b28148e3171
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Loka"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Stækka"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Stillingar"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Spila"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Gera hlé"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Forrit styður ekki opnun á öðrum skjá."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Skjáskipting"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Vinstri á öllum skjánum"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vinstri 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vinstri 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vinstri 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Hægri á öllum skjánum"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Efri á öllum skjánum"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Efri 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Efri 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Efri 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Neðri á öllum skjánum"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Notkun einhentrar stillingar"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Til að loka skaltu strjúka upp frá neðri hluta skjásins eða ýta hvar sem er fyrir ofan forritið"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Ræsa einhenta stillingu"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Hætta í einhentri stillingu"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Stillingar fyrir blöðrur frá <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Yfirflæði"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Bæta aftur í stafla"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> frá <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ frá <xliff:g id="APP_NAME">%2$s</xliff:g> og <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> í viðbót"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Færa efst til vinstri"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Færa efst til hægri"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Færa neðst til vinstri"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Færðu neðst til hægri"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Stillingar <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Loka blöðru"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ekki setja samtal í blöðru"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Spjalla með blöðrum"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Ný samtöl birtast sem fljótandi tákn eða blöðrur. Ýttu til að opna blöðru. Dragðu hana til að færa."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Hægt er að stjórna blöðrum hvenær sem er"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Ýttu á „Stjórna“ til að slökkva á blöðrum frá þessu forriti"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ég skil"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Engar nýlegar blöðrur"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nýlegar blöðrur og blöðrur sem þú hefur lokað birtast hér"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings_tv.xml b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
new file mode 100644
index 000000000000..1f148d948a0e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-is/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Mynd í mynd"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Efni án titils)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Loka mynd í mynd"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Allur skjárinn"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
new file mode 100644
index 000000000000..8a2b9dbd9ba8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Chiudi"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Espandi"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Impostazioni"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Riproduci"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Metti in pausa"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"L\'app non supporta l\'avvio su display secondari."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Strumento per schermo diviso"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Schermata sinistra a schermo intero"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Schermata sinistra al 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Schermata sinistra al 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Schermata sinistra al 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Schermata destra a schermo intero"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Schermata superiore a schermo intero"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Schermata superiore al 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Schermata superiore al 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Schermata superiore al 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Schermata inferiore a schermo intero"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usare la modalità one-hand"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Per uscire, scorri verso l\'alto dalla parte inferiore dello schermo oppure tocca un punto qualsiasi sopra l\'app"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Avvia la modalità one-hand"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Esci dalla modalità one-hand"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Impostazioni per bolle <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Altre"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Aggiungi di nuovo all\'elenco"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> da <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> da <xliff:g id="APP_NAME">%2$s</xliff:g> e altre <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Sposta in alto a sinistra"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sposta in alto a destra"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sposta in basso a sinistra"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sposta in basso a destra"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Impostazioni <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string>
+ <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_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>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nessuna bolla recente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Le bolle recenti e ignorate appariranno qui"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings_tv.xml b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
new file mode 100644
index 000000000000..127454cf28bf
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-it/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture in picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
new file mode 100644
index 000000000000..82ba22028141
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"סגירה"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"הרחב"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"הגדרות"</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_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="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>
+ <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_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_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="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>"</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="NOTIFICATION_TITLE">%1$s</xliff:g> מהאפליקציה <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מ-<xliff:g id="APP_NAME">%2$s</xliff:g> ועוד <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"העברה לפינה השמאלית העליונה"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"העברה לפינה הימנית העליונה"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"העברה לפינה השמאלית התחתונה"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"העברה לפינה הימנית התחתונה"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"הגדרות <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"סגירת בועה"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"אין להציג בועות לשיחה"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"לדבר בבועות"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"שיחות חדשות מופיעות כסמלים צפים, או בועות. יש להקיש כדי לפתוח בועה. יש לגרור כדי להזיז אותה."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"שליטה בבועות, בכל זמן"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"יש להקיש על \'ניהול\' כדי להשבית את הבועות מהאפליקציה הזו"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"הבנתי"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"אין בועות מהזמן האחרון"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"בועות אחרונות ובועות שנסגרו יופיעו כאן"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
new file mode 100644
index 000000000000..8ca54e0a5473
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"‏סגור PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
new file mode 100644
index 000000000000..fbb2951a06e1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"閉じる"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"設定"</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>
+ <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="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>
+ <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_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_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="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> のバブルの設定"</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="NOTIFICATION_TITLE">%1$s</xliff:g>(<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>(<xliff:g id="APP_NAME">%2$s</xliff:g>)、他 <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> 件"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"左上に移動"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"右上に移動"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"左下に移動"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"右下に移動"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> の設定"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"バブルを閉じる"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"チャットでバブルを使う"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"いつでもバブルを管理"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"このアプリからのバブルを OFF にするには、[管理] をタップしてください"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近閉じたバブルはありません"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近表示されたバブルや閉じたバブルが、ここに表示されます"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings_tv.xml b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
new file mode 100644
index 000000000000..b7ab28c44fd2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ja/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(無題の番組)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP を閉じる"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"全画面表示"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
new file mode 100644
index 000000000000..f978481be23d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"დახურვა"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"გაშლა"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"პარამეტრები"</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_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="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>
+ <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_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_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="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> ბუშტებისთვის"</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="NOTIFICATION_TITLE">%1$s</xliff:g> <xliff:g id="APP_NAME">%2$s</xliff:g>-ისგან"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> <xliff:g id="APP_NAME">%2$s</xliff:g>-დან და კიდევ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ზევით და მარცხნივ გადატანა"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"გადაანაცვლეთ ზევით და მარჯვნივ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ქვევით და მარცხნივ გადატანა"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"გადაანაცვ. ქვემოთ და მარჯვნივ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-ის პარამეტრები"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ბუშტის დახურვა"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"აიკრძალოს საუბრის ბუშტები"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ჩეთი ბუშტების გამოყენებით"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ახალი საუბრები გამოჩნდება როგორც მოტივტივე ხატულები ან ბუშტები. შეეხეთ ბუშტის გასახსნელად. გადაიტანეთ ჩავლებით."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ბუშტების ნებისმიერ დროს გაკონტროლება"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ამ აპის ბუშტების გამოსართავად შეეხეთ „მართვას“"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"გასაგებია"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ბოლო დროს გამოყენებული ბუშტები არ არის"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"აქ გამოჩნდება ბოლოდროინდელი ბუშტები და უარყოფილი ბუშტები"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings_tv.xml b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
new file mode 100644
index 000000000000..1bf4b8ebdcda
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ka/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(პროგრამის სათაურის გარეშე)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP-ის დახურვა"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"სრულ ეკრანზე"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
new file mode 100644
index 000000000000..2d27fafcc98b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Жабу"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Жаю"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Параметрлер"</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>
+ <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="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>
+ <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_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_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="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> қалқыма хабарларының параметрлері"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> қолданбасы жіберген <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> және тағы <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Жоғарғы сол жаққа жылжыту"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жоғары оң жаққа жылжыту"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төменгі сол жаққа жылжыту"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төменгі оң жаққа жылжыту"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлері"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Қалқымалы хабарды жабу"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Әңгіменің қалқыма хабары көрсетілмесін"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Қалқыма хабарлар арқылы сөйлесу"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңа әңгімелер қалқыма белгішелер немесе хабарлар түрінде көрсетіледі. Қалқыма хабарды ашу үшін түртіңіз. Жылжыту үшін сүйреңіз."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Қалқыма хабарларды реттеу"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бұл қолданбадан қалқыма хабарларды өшіру үшін \"Басқару\" түймесін түртіңіз."</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түсінікті"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Жақындағы қалқыма хабарлар жоқ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқымалы анықтама өшірілді."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings_tv.xml b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
new file mode 100644
index 000000000000..8f1e725e79e2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-kk/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(Атаусыз бағдарлама)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP жабу"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Толық экран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
new file mode 100644
index 000000000000..d503b7a5edca
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"បិទ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ពង្រីក"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ការកំណត់"</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_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="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>
+ <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_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_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="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>"</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="NOTIFICATION_TITLE">%1$s</xliff:g> ពី <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ពី <xliff:g id="APP_NAME">%2$s</xliff:g> និង <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ទៀត"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ផ្លាស់ទីទៅផ្នែកខាងលើខាងឆ្វេង"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ផ្លាស់ទីទៅផ្នែកខាងលើខាងស្ដាំ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងឆ្វេង​"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ផ្លាស់ទីទៅផ្នែកខាងក្រោម​ខាងស្ដាំ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"ការកំណត់ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ច្រានចោល​ពពុះ"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"កុំបង្ហាញ​ការសន្ទនា​ជាពពុះ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ជជែក​ដោយប្រើ​ពពុះ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ការសន្ទនាថ្មីៗ​បង្ហាញជា​​ពពុះ ឬរូបអណ្ដែត។ ចុច ដើម្បីបើក​ពពុះ។ អូស ដើម្បី​ផ្លាស់ទី​ពពុះនេះ។"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"គ្រប់គ្រង​​ពពុះ​បានគ្រប់ពេល"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ចុច \"គ្រប់គ្រង\" ដើម្បីបិទ​ពពុះពីកម្មវិធីនេះ"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"យល់ហើយ"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"មិនមាន​ពពុះ​ថ្មីៗ​ទេ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ពពុះថ្មីៗ​ និង​ពពុះដែលបានបិទ​​នឹង​បង្ហាញ​នៅទីនេះ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោល​សារលេចឡើង។"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings_tv.xml b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
new file mode 100644
index 000000000000..b55997056e66
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-km/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(កម្មវិធី​គ្មានចំណងជើង)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"បិទ PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ពេញអេក្រង់"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
new file mode 100644
index 000000000000..3d61d84f4810
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"ಮುಚ್ಚಿ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ವಿಸ್ತೃತಗೊಳಿಸು"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</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_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="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>
+ <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_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_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="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> ಬಬಲ್ಸ್‌ಗಾಗಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> ಮತ್ತು <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ಹೆಚ್ಚಿನವುಗಳ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ಎಡ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"ಬಬಲ್ ವಜಾಗೊಳಿಸಿ"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ಸಂಭಾಷಣೆಯನ್ನು ಬಬಲ್ ಮಾಡಬೇಡಿ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ಬಬಲ್ಸ್ ಬಳಸಿ ಚಾಟ್ ಮಾಡಿ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ಹೊಸ ಸಂಭಾಷಣೆಗಳು ತೇಲುವ ಐಕಾನ್‌ಗಳು ಅಥವಾ ಬಬಲ್ಸ್ ಆಗಿ ಗೋಚರಿಸುತ್ತವೆ. ಬಬಲ್ ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಅದನ್ನು ಡ್ರ್ಯಾಗ್ ಮಾಡಲು ಎಳೆಯಿರಿ."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಬಬಲ್ಸ್ ಅನ್ನು ನಿಯಂತ್ರಿಸಿ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ಈ ಆ್ಯಪ್‌ನಿಂದ ಬಬಲ್ಸ್ ಅನ್ನು ಆಫ್ ಮಾಡಲು ನಿರ್ವಹಿಸಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ಅರ್ಥವಾಯಿತು"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಬಬಲ್ಸ್ ಇಲ್ಲ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ಇತ್ತೀಚಿನ ಬಬಲ್ಸ್ ಮತ್ತು ವಜಾಗೊಳಿಸಿದ ಬಬಲ್ಸ್ ಇಲ್ಲಿ ಗೋಚರಿಸುತ್ತವೆ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
new file mode 100644
index 000000000000..9d3942fa4dd3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP ಮುಚ್ಚಿ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ಪೂರ್ಣ ಪರದೆ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
new file mode 100644
index 000000000000..ea7ad56bf9d2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"닫기"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"펼치기"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"설정"</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>
+ <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="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>
+ <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_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_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="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> 대화창 설정"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> 외 <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>개의 <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"왼쪽 상단으로 이동"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"오른쪽 상단으로 이동"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"대화창 닫기"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"대화를 대화창으로 표시하지 않기"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"대화창으로 채팅하기"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"새로운 대화가 플로팅 아이콘인 대화창으로 표시됩니다. 대화창을 열려면 탭하세요. 드래그하여 이동할 수 있습니다."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"언제든지 대화창을 제어하세요"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"이 앱에서 대화창을 사용 중지하려면 관리를 탭하세요."</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"확인"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"최근 대화창 없음"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"최근 대화창과 내가 닫은 대화창이 여기에 표시됩니다."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings_tv.xml b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
new file mode 100644
index 000000000000..46d6ad4e0b0f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ko/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"PIP 모드"</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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
new file mode 100644
index 000000000000..611b2d60a8c1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Жабуу"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Жайып көрсөтүү"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Жөндөөлөр"</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_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="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>
+ <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_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_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="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> калкып чыкма билдирмелер жөндөөлөрү"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> жана дагы <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> колдонмодон <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Жогорку сол жакка жылдыруу"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жогорку оң жакка жылдырыңыз"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"Калкып чыкма билдирмени жабуу"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Жазышууда калкып чыкма билдирмелер көрүнбөсүн"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн, \"Башкарууну\" басыңыз"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings_tv.xml b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
new file mode 100644
index 000000000000..d5d1d7ef914e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ky/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(Аталышы жок программа)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP\'ти жабуу"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Толук экран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
new file mode 100644
index 000000000000..a1c998c078de
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"ປິດ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ຂະຫຍາຍ"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ການຕັ້ງຄ່າ"</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_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="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>
+ <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_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_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="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>"</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="NOTIFICATION_TITLE">%1$s</xliff:g> ຈາກ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ຈາກ <xliff:g id="APP_NAME">%2$s</xliff:g> ແລະ ອີກ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ຍ້າຍຊ້າຍເທິງ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ຍ້າຍຂວາເທິງ"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ຍ້າຍຊ້າຍລຸ່ມ"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ຍ້າຍຂວາລຸ່ມ"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"ການຕັ້ງຄ່າ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ປິດຟອງໄວ້"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ຢ່າໃຊ້ຟອງໃນການສົນທະນາ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ສົນທະນາໂດຍໃຊ້ຟອງ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ການສົນທະນາໃໝ່ຈະປາກົດເປັນໄອຄອນ ຫຼື ຟອງແບບລອຍ. ແຕະເພື່ອເປີດຟອງ. ລາກເພື່ອຍ້າຍມັນ."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ຄວບຄຸມຟອງຕອນໃດກໍໄດ້"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ແຕະຈັດການ ເພື່ອປິດຟອງຈາກແອັບນີ້"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ເຂົ້າໃຈແລ້ວ"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ບໍ່ມີຟອງຫຼ້າສຸດ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ຟອງຫຼ້າສຸດ ແລະ ຟອງທີ່ປິດໄປຈະປາກົດຢູ່ບ່ອນນີ້"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings_tv.xml b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
new file mode 100644
index 000000000000..f6362c120b9f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lo/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(ໂປຣແກຣມບໍ່ມີຊື່)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"ປິດ PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ເຕັມໜ້າຈໍ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
new file mode 100644
index 000000000000..b2ccd5709e21
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_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>
+ <string name="pip_play" msgid="3496151081459417097">"Leisti"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pristabdyti"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Programa nepalaiko paleisties antriniuose ekranuose."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Skaidyto ekrano daliklis"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Kairysis ekranas viso ekrano režimu"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kairysis ekranas 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kairysis ekranas 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kairysis ekranas 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Dešinysis ekranas viso ekrano režimu"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Viršutinis ekranas viso ekrano režimu"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Viršutinis ekranas 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Viršutinis ekranas 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Viršutinis ekranas 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Apatinis ekranas viso ekrano režimu"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Vienos rankos režimo naudojimas"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Jei norite išeiti, perbraukite aukštyn nuo ekrano apačios arba palieskite bet kur virš programos"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Pradėti vienos rankos režimą"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Išeiti iš vienos rankos režimo"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ burbulų nustatymai"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Perpildymas"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Pridėti atgal į krūvą"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ iš „<xliff:g id="APP_NAME">%2$s</xliff:g>“"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ iš „<xliff:g id="APP_NAME">%2$s</xliff:g>“ ir dar <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Perkelti į viršų kairėje"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Perkelti į viršų dešinėje"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Perkelti į apačią kairėje"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Perkelti į apačią dešinėje"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ nustatymai"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Atsisakyti burbulo"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerodyti pokalbio burbule"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Pokalbis naudojant burbulus"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nauji pokalbiai rodomi kaip slankiosios piktogramos arba burbulai. Palieskite, kad atidarytumėte burbulą. Vilkite, kad perkeltumėte."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bet kada valdyti burbulus"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Palieskite „Tvarkyti“, kad išjungtumėte burbulus šioje programoje"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Supratau"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nėra naujausių burbulų"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Naujausi ir atsisakyti burbulai bus rodomi čia"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings_tv.xml b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
new file mode 100644
index 000000000000..e4695a05f038
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lt/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Vaizdas vaizde"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programa be pavadinimo)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Uždaryti PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Visas ekranas"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
new file mode 100644
index 000000000000..e6d0c7725bbf
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_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>
+ <string name="pip_play" msgid="3496151081459417097">"Atskaņot"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Apturēt"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Lietotnē netiek atbalstīta palaišana sekundārajos displejos."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Ekrāna sadalītājs"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Kreisā daļa pa visu ekrānu"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Pa kreisi 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Pa kreisi 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Pa kreisi 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Labā daļa pa visu ekrānu"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Augšdaļa pa visu ekrānu"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Augšdaļa 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Augšdaļa 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Augšdaļa 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Apakšdaļu pa visu ekrānu"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Vienas rokas režīma izmantošana"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Lai izietu, velciet augšup no ekrāna apakšdaļas vai pieskarieties jebkurā vietā virs lietotnes"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Pāriet vienas rokas režīmā"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Iziet no vienas rokas režīma"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> burbuļu iestatījumi"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Pārpilde"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Pievienot atpakaļ kopai"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> no: <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> no lietotnes “<xliff:g id="APP_NAME">%2$s</xliff:g>” un vēl <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Pārvietot augšpusē pa kreisi"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pārvietot augšpusē pa labi"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pārvietot apakšpusē pa kreisi"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pārvietot apakšpusē pa labi"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Lietotnes <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iestatījumi"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Nerādīt burbuli"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerādīt sarunu burbuļos"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Tērzēšana, izmantojot burbuļus"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Jaunas sarunas tiek rādītas kā peldošas ikonas vai burbuļi. Pieskarieties, lai atvērtu burbuli. Velciet, lai to pārvietotu."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Allaž pārvaldīt burbuļus"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Pieskarieties pogai “Pārvaldīt”, lai izslēgtu burbuļus no šīs lietotnes."</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Labi"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nav nesen aizvērtu burbuļu"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Šeit būs redzami nesen rādītie burbuļi un aizvērtie burbuļi"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings_tv.xml b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
new file mode 100644
index 000000000000..f2b037fbeeee
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-lv/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Attēls attēlā"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programma bez nosaukuma)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Aizvērt PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pilnekrāna režīms"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
new file mode 100644
index 000000000000..43f2881fd553
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Затвори"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Проширете"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Поставки"</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_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="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>
+ <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_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_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="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>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Прелевање"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Додајте назад во stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> од <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> од <xliff:g id="APP_NAME">%2$s</xliff:g> и уште <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Премести горе лево"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести долу лево"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести долу десно"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Поставки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Отфрли балонче"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не прикажувај го разговорот во балончиња"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Разговор во балончиња"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новите разговори ќе се појавуваат како лебдечки икони или балончиња. Допрете за отворање на балончето. Повлечете за да го преместите."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Контролирајте ги балончињата во секое време"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Допрете „Управувајте“ за да ги исклучите балончињата од апликацијава"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Сфатив"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема неодамнешни балончиња"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Неодамнешните и отфрлените балончиња ќе се појавуваат тука"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings_tv.xml b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
new file mode 100644
index 000000000000..25dc764f4d5e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mk/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без наслов)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Цел екран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
new file mode 100644
index 000000000000..dfa4b2287b10
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"അവസാനിപ്പിക്കുക"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"വികസിപ്പിക്കുക"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ക്രമീകരണം"</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_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>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <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_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_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>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബബിളുകളുടെ ക്രമീകരണം"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"ഓവർഫ്ലോ"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"അടുക്കുകളിലേക്ക് തിരിച്ച് ചേർക്കുക"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g>-ൽ നിന്നുള്ള <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> എന്നതിൽ നിന്നുള്ള <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> കൂടുതലും"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"മുകളിൽ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ചുവടെ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ചുവടെ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ക്രമീകരണം"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ബബിൾ ഡിസ്മിസ് ചെയ്യൂ"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"സംഭാഷണം ബബിൾ ചെയ്യരുത്"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ബബിളുകൾ ഉപയോഗിച്ച് ചാറ്റ് ചെയ്യുക"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"പുതിയ സംഭാഷണങ്ങൾ ഫ്ലോട്ടിംഗ് ഐക്കണുകളോ ബബിളുകളോ ആയി ദൃശ്യമാവുന്നു. ബബിൾ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ഇത് നീക്കാൻ വലിച്ചിടുക."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ബബിളുകൾ ഏതുസമയത്തും നിയന്ത്രിക്കുക"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ഈ ആപ്പിൽ നിന്നുള്ള ബബിളുകൾ ഓഫാക്കാൻ മാനേജ് ചെയ്യുക ടാപ്പ് ചെയ്യുക"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ബബ്ൾ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബ്ൾ ഡിസ്മിസ് ചെയ്തു."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings_tv.xml b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
new file mode 100644
index 000000000000..7aaf79fb666e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ml/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(പേരില്ലാത്ത പ്രോഗ്രാം)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP അടയ്ക്കുക"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"പൂര്‍ണ്ണ സ്ക്രീന്‍"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
new file mode 100644
index 000000000000..044fd9fa7544
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Хаах"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Дэлгэх"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Тохиргоо"</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_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="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>
+ <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_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_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="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>-н бөмбөлгүүдийн тохиргоо"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g>-н <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> болон бусад <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Зүүн дээш зөөх"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Баруун дээш зөөх"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Зүүн доош зөөх"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Баруун доош зөөх"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-н тохиргоо"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Бөмбөлгийг хаах"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Харилцан яриаг бүү бөмбөлөг болго"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Бөмбөлөг ашиглан чатлаарай"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Шинэ харилцан яриа нь хөвөгч дүрс тэмдэг эсвэл бөмбөлөг хэлбэрээр харагддаг. Бөмбөлгийг нээхийн тулд товшино уу. Түүнийг зөөхийн тулд чирнэ үү."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Дурын үед бөмбөлгийг хянаарай"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Энэ аппын бөмбөлгүүдийг унтраахын тулд Удирдах дээр товшино уу"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ойлголоо"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Саяхны бөмбөлөг алга байна"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Саяхны бөмбөлгүүд болон үл хэрэгссэн бөмбөлгүүд энд харагдана"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings_tv.xml b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
new file mode 100644
index 000000000000..55519d462b69
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mn/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(Гарчиггүй хөтөлбөр)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP-г хаах"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Бүтэн дэлгэц"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
new file mode 100644
index 000000000000..e838cf59331e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"बंद करा"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"विस्तृत करा"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"सेटिंग्ज"</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_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="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>
+ <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_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_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">"शीर्ष 10"</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> बबलसाठी सेटिंग्ज"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> आणि आणखी <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> कडून <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"वर डावीकडे हलवा"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"वर उजवीकडे हलवा"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"बबल डिसमिस करा"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"संभाषणाला बबल करू नका"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"बबल वापरून चॅट करा"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"नवीन संभाषणे फ्लोटिंग आयकन किंवा बबल म्हणून दिसतात. बबल उघडण्यासाठी टॅप करा. हे हलवण्यासाठी ड्रॅग करा."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"बबल कधीही नियंत्रित करा"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"या अ‍ॅपमधून बबल बंद करण्यासाठी व्यवस्थापित करा वर टॅप करा"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"समजले"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"अलीकडील कोणतेही बबल नाहीत"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"अलीकडील बबल आणि डिसमिस केलेले बबल येथे दिसतील"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings_tv.xml b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
new file mode 100644
index 000000000000..ad2cfc6035c2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-mr/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षक नसलेला कार्यक्रम)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP बंद करा"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रीन"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
new file mode 100644
index 000000000000..6664f38f3879
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Tutup"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Kembangkan"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Tetapan"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Main"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Jeda"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Apl tidak menyokong pelancaran pada paparan kedua."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Pembahagi skrin pisah"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Skrin penuh kiri"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kiri 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kiri 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kiri 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Skrin penuh kanan"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Skrin penuh atas"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Atas 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Atas 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Atas 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Skrin penuh bawah"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Menggunakan mod sebelah tangan"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Untuk keluar, leret ke atas daripada bahagian bawah skrin atau ketik pada mana-mana di bahagian atas apl"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Mulakan mod sebelah tangan"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Keluar daripada mod sebelah tangan"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Tetapan untuk gelembung <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Limpahan"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Tambah kembali pada tindanan"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> daripada <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> daripada <xliff:g id="APP_NAME">%2$s</xliff:g> dan <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> lagi"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Alihkan ke atas sebelah kiri"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Alihkan ke atas sebelah kanan"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Alihkan ke bawah sebelah kiri"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Alihkan ke bawah sebelah kanan"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Tetapan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ketepikan gelembung"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan jadikan perbualan dalam bentuk gelembung"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bersembang menggunakan gelembung"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Perbualan baharu muncul sebagai ikon terapung atau gelembung. Ketik untuk membuka gelembung. Seret untuk mengalihkan gelembung tersebut."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kawal gelembung pada bila-bila masa"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Ketik Urus untuk mematikan gelembung daripada apl ini"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tiada gelembung terbaharu"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Gelembung baharu dan gelembung yang diketepikan akan dipaparkan di sini"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings_tv.xml b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
new file mode 100644
index 000000000000..b2d7214381ef
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ms/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Gambar dalam Gambar"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program tiada tajuk)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Tutup PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Skrin penuh"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
new file mode 100644
index 000000000000..9681d14a6a88
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"ပိတ်ရန်"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ချဲ့ရန်"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ဆက်တင်များ"</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_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="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>
+ <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_70" msgid="8859845045360659250">"ဘယ်ဘက်မျက်နှာပြင် ၇၀%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ဘယ်ဘက် မျက်နှာပြင် ၅၀%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ဘယ်ဘက် မျက်နှာပြင် ၃၀%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ညာဘက် မျက်နှာပြင်အပြည့်"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"အပေါ်ဘက် မျက်နှာပြင်အပြည့်"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"အပေါ်ဘက် မျက်နှာပြင် ၇၀%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"အပေါ်ဘက် မျက်နှာပြင် ၅၀%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"အပေါ်ဘက် မျက်နှာပြင် ၃၀%"</string>
+ <string name="accessibility_action_divider_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> ပူဖောင်းကွက်အတွက် ဆက်တင်များ"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> နှင့် နောက်ထပ် <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ခုမှ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ဘယ်ဘက်ထိပ်သို့ ရွှေ့ရန်"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ညာဘက်ထိပ်သို့ ရွှေ့ပါ"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"ပူဖောင်းကွက် ပယ်ရန်"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"စကားဝိုင်းကို ပူဖောင်းကွက် မပြုလုပ်ပါနှင့်"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ပူဖောင်းကွက် သုံး၍ ချတ်လုပ်ခြင်း"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"စကားဝိုင်းအသစ်များကို မျောနေသည့် သင်္ကေတများ သို့မဟုတ် ပူဖောင်းကွက်များအဖြစ် မြင်ရပါမည်။ ပူဖောင်းကွက်ကိုဖွင့်ရန် တို့ပါ။ ရွှေ့ရန် ၎င်းကို ဖိဆွဲပါ။"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ပူဖောင်းကွက်ကို အချိန်မရွေး ထိန်းချုပ်ရန်"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ဤအက်ပ်မှနေ၍ ပူဖောင်းများကို ပိတ်ရန်အတွက် \'စီမံရန်\' ကို တို့ပါ"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ရပြီ"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"လတ်တလော ပူဖောင်းကွက်များ မရှိပါ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"လတ်တလော ပူဖောင်းကွက်များနှင့် ပိတ်လိုက်သော ပူဖောင်းကွက်များကို ဤနေရာတွင် မြင်ရပါမည်"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings_tv.xml b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
new file mode 100644
index 000000000000..9569dc4cbeea
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-my/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(ခေါင်းစဉ်မဲ့ အစီအစဉ်)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP ကိုပိတ်ပါ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"မျက်နှာပြင် အပြည့်"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
new file mode 100644
index 000000000000..986e890dfe3a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Lukk"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Vis"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Innstillinger"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Spill av"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Sett på pause"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan ikke kjøres på sekundære skjermer."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Skilleelement for delt skjerm"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Utvid den venstre delen av skjermen til hele skjermen"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Sett størrelsen på den venstre delen av skjermen til 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Sett størrelsen på den venstre delen av skjermen til 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Sett størrelsen på den venstre delen av skjermen til 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Utvid den høyre delen av skjermen til hele skjermen"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Utvid den øverste delen av skjermen til hele skjermen"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Sett størrelsen på den øverste delen av skjermen til 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Sett størrelsen på den øverste delen av skjermen til 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Sett størrelsen på den øverste delen av skjermen til 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Utvid den nederste delen av skjermen til hele skjermen"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bruk av enhåndsmodus"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"For å avslutte, sveip opp fra bunnen av skjermen eller trykk hvor som helst over appen"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Start enhåndsmodus"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Avslutt enhåndsmodus"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Innstillinger for <xliff:g id="APP_NAME">%1$s</xliff:g>-bobler"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflyt"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Legg tilbake i stabelen"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> fra <xliff:g id="APP_NAME">%2$s</xliff:g> og <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> flere"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Flytt til øverst til venstre"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytt til øverst til høyre"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytt til nederst til venstre"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytt til nederst til høyre"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-innstillinger"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Lukk boblen"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ikke vis samtaler i bobler"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat med bobler"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nye samtaler vises som flytende ikoner eller bobler. Trykk for å åpne bobler. Dra for å flytte dem."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrollér bobler når som helst"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Trykk på Administrer for å slå av bobler for denne appen"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Greit"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen nylige bobler"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nylige bobler og avviste bobler vises her"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings_tv.xml b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
new file mode 100644
index 000000000000..8a7f315606ad
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-nb/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Bilde-i-bilde"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program uten tittel)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Lukk PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Fullskjerm"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
new file mode 100644
index 000000000000..0369c6dd2831
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"बन्द गर्नुहोस्"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"विस्तृत गर्नुहोस्"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"सेटिङहरू"</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>
+ <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="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>
+ <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_70" msgid="8859845045360659250">"बायाँ भाग ७०%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"बायाँ भाग ५०%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"बायाँ भाग ३०%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"दायाँ भाग फुल स्क्रिन"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"माथिल्लो भाग फुल स्क्रिन"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"माथिल्लो भाग ७०%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"माथिल्लो भाग ५०%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"माथिल्लो भाग ३०%"</string>
+ <string name="accessibility_action_divider_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> का बबलसम्बन्धी सेटिङहरू"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> का <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> र थप <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"शीर्ष भागको बायाँतिर सार्नुहोस्"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सिरानमा दायाँतिर सार्नुहोस्"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"पुछारमा बायाँतिर सार्नुहोस्"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"पुछारमा दायाँतिर सार्नुहोस्"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> का सेटिङहरू"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारेज गर्नुहोस्"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"वार्तालाप बबलको रूपमा नदेखाइयोस्"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"बबलहरू प्रयोग गरी कुराकानी गर्नुहोस्"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"नयाँ वार्तालापहरू तैरने आइकन वा बबलका रूपमा देखिन्छन्। बबल खोल्न ट्याप गर्नुहोस्। बबल सार्न सो बबललाई ड्र्याग गर्नुहोस्।"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"जुनसुकै बेला बबलहरू नियन्त्रण गर्नुहोस्"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"यो एपबाट आएका बबलहरू अफ गर्न \"व्यवस्थापन गर्नुहोस्\" बटनमा ट्याप गर्नुहोस्"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"बुझेँ"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हालैका बबलहरू छैनन्"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हालैका बबल र खारेज गरिएका बबलहरू यहाँ देखिने छन्"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings_tv.xml b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
new file mode 100644
index 000000000000..87fa3279f05e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ne/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(शीर्षकविहीन कार्यक्रम)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP लाई बन्द गर्नुहोस्"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"फुल स्क्रिन"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
new file mode 100644
index 000000000000..26c276e7e690
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Sluiten"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Uitvouwen"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Instellingen"</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 schakel je de functie uit."</string>
+ <string name="pip_play" msgid="3496151081459417097">"Afspelen"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Onderbreken"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"App kan niet op secundaire displays worden gestart."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Scheiding voor gesplitst scherm"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Linkerscherm op volledig scherm"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Linkerscherm 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Linkerscherm 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Linkerscherm 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Rechterscherm op volledig scherm"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Bovenste scherm op volledig scherm"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Bovenste scherm 70%"</string>
+ <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_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="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>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> van <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> van <xliff:g id="APP_NAME">%2$s</xliff:g> en nog <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Naar linksboven verplaatsen"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Naar rechtsboven verplaatsen"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Naar linksonder verplaatsen"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels weergeven"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatten met bubbels"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden weergegeven als zwevende iconen of \'bubbels\'. Tik om een bubbel te openen. Sleep om de bubbel te verplaatsen."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Beheer bubbels wanneer je wilt"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te schakelen"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels worden hier weergegeven"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings_tv.xml b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
new file mode 100644
index 000000000000..df3809e5d6c6
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-nl/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Scherm-in-scherm"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Naamloos programma)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP sluiten"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Volledig scherm"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
new file mode 100644
index 000000000000..27f16226a421
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ବଢ଼ାନ୍ତୁ"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ସେଟିଂସ୍"</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_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="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>
+ <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_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_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="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> ବବଲ୍‌ଗୁଡ଼ିକ ପାଇଁ ସେଟିଂସ୍"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> ଏବଂ ଅଧିକ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>ଟିରୁ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ଉପର ବାମକୁ ନିଅନ୍ତୁ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ଉପର-ଡାହାଣକୁ ନିଅନ୍ତୁ"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"ବବଲ୍ ଖାରଜ କରନ୍ତୁ"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ବାର୍ତ୍ତାଳାପକୁ ବବଲ୍ କରନ୍ତୁ ନାହିଁ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ବବଲଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି ଚାଟ୍ କରନ୍ତୁ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ନୂଆ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଫ୍ଲୋଟିଂ ଆଇକନ୍ କିମ୍ବା ବବଲ୍ ଭାବେ ଦେଖାଯିବ। ବବଲ୍ ଖୋଲିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଏହାକୁ ମୁଭ୍ କରିବାକୁ ଟାଣନ୍ତୁ।"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ଯେ କୌଣସି ସମୟରେ ବବଲଗୁଡ଼ିକ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ଏହି ଆପର ବବଲଗୁଡ଼ିକ ବନ୍ଦ କରିବା ପାଇଁ \'ପରିଚାଳନା କରନ୍ତୁ\' ବଟନରେ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ବୁଝିଗଲି"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ବର୍ତ୍ତମାନ କୌଣସି ବବଲ୍ ନାହିଁ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ବର୍ତ୍ତମାନର ଏବଂ ଖାରଜ କରାଯାଇଥିବା ବବଲଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯିବ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings_tv.xml b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
new file mode 100644
index 000000000000..295a5c4ee1ce
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-or/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(କୌଣସି ଟାଇଟଲ୍‍ ପ୍ରୋଗ୍ରାମ୍‍ ନାହିଁ)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
new file mode 100644
index 000000000000..96688b952d66
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"ਬੰਦ ਕਰੋ"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ਵਿਸਤਾਰ ਕਰੋ"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ਸੈਟਿੰਗਾਂ"</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_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="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>
+ <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_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_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="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> ਬਬਲ ਲਈ ਸੈਟਿੰਗਾਂ"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> ਅਤੇ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ਹੋਰਾਂ ਤੋਂ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ਉੱਪਰ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ਗੱਲਬਾਤ \'ਤੇ ਬਬਲ ਨਾ ਲਾਓ"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"ਬਬਲ ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"ਨਵੀਆਂ ਗੱਲਾਂਬਾਤਾਂ ਫਲੋਟਿੰਗ ਪ੍ਰਤੀਕਾਂ ਜਾਂ ਬਬਲ ਦੇ ਰੂਪ ਵਿੱਚ ਦਿਸਦੀਆਂ ਹਨ। ਬਬਲ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ। ਇਸਨੂੰ ਲਿਜਾਣ ਲਈ ਘਸੀਟੋ।"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ਬਬਲ ਨੂੰ ਕਿਸੇ ਵੇਲੇ ਵੀ ਕੰਟਰੋਲ ਕਰੋ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ਇਸ ਐਪ \'ਤੇ ਬਬਲ ਬੰਦ ਕਰਨ ਲਈ \'ਪ੍ਰਬੰਧਨ ਕਰੋ\' \'ਤੇ ਟੈਪ ਕਰੋ"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ਸਮਝ ਲਿਆ"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ਕੋਈ ਹਾਲੀਆ ਬਬਲ ਨਹੀਂ"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ਹਾਲੀਆ ਬਬਲ ਅਤੇ ਖਾਰਜ ਕੀਤੇ ਬਬਲ ਇੱਥੇ ਦਿਸਣਗੇ"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings_tv.xml b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
new file mode 100644
index 000000000000..e32895a9a239
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pa/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(ਸਿਰਲੇਖ-ਰਹਿਤ ਪ੍ਰੋਗਰਾਮ)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP ਬੰਦ ਕਰੋ"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
new file mode 100644
index 000000000000..6b640b54f898
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Zamknij"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Rozwiń"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ustawienia"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Odtwórz"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Wstrzymaj"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacja nie obsługuje uruchamiania na dodatkowych ekranach."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Linia dzielenia ekranu"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lewa część ekranu na pełnym ekranie"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% lewej części ekranu"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% lewej części ekranu"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% lewej części ekranu"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Prawa część ekranu na pełnym ekranie"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Górna część ekranu na pełnym ekranie"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% górnej części ekranu"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% górnej części ekranu"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% górnej części ekranu"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Dolna część ekranu na pełnym ekranie"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Korzystanie z trybu jednej ręki"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Aby zamknąć, przesuń palcem z dołu ekranu w górę lub kliknij dowolne miejsce nad aplikacją"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Uruchom tryb jednej ręki"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Zamknij tryb jednej ręki"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ustawienia dymków aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Przepełnienie"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodaj ponownie do stosu"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikacji <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikacji <xliff:g id="APP_NAME">%2$s</xliff:g> i jeszcze <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Przenieś w lewy górny róg"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Przenieś w prawy górny róg"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Przenieś w lewy dolny róg"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Przenieś w prawy dolny róg"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – ustawienia"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zamknij dymek"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nie wyświetlaj rozmowy jako dymka"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Czatuj, korzystając z dymków"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nowe rozmowy będą wyświetlane jako pływające ikony lub dymki. Kliknij, by otworzyć dymek. Przeciągnij, by go przenieść."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Zarządzaj dymkami w dowolnym momencie"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Kliknij Zarządzaj, aby wyłączyć dymki z tej aplikacji"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Brak ostatnich dymków"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tutaj będą pojawiać się ostatnie i odrzucone dymki"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings_tv.xml b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
new file mode 100644
index 000000000000..286fd7b2ff0f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pl/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Obraz w obrazie"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez tytułu)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Zamknij PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Pełny ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..465d2d17a5e7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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_settings" msgid="5468987116750491918">"Configurações"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Reproduzir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"O app não é compatível com a inicialização em telas secundárias."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor de tela"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lado esquerdo em tela cheia"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Esquerda a 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Esquerda a 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Esquerda a 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Lado direito em tela cheia"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Parte superior em tela cheia"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Parte superior a 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Parte superior a 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Parte superior a 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Parte inferior em tela cheia"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo para uma mão"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo para uma mão"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo para uma mão"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configurações de balões do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu flutuante"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Devolver à pilha"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> mais <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover para canto superior esquerdo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse usando balões"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controle os balões a qualquer momento"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toque em \"Gerenciar\" para desativar os balões desse app"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e dispensados aparecerão aqui"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+</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
new file mode 100644
index 000000000000..57edcdf74cf4
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..df841bf3eda4
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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_settings" msgid="5468987116750491918">"Definições"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Reproduzir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Mudar para o seguinte"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"A app não é compatível com o início em ecrãs secundários."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor do ecrã dividido"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ecrã esquerdo inteiro"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% no ecrã esquerdo"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% no ecrã esquerdo"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% no ecrã esquerdo"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Ecrã direito inteiro"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ecrã superior inteiro"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% no ecrã superior"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% no ecrã superior"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% no ecrã superior"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ecrã inferior inteiro"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utilize o modo para uma mão"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para sair, deslize rapidamente para cima a partir da parte inferior do ecrã ou toque em qualquer ponto acima da app."</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo para uma mão"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo para uma mão"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Definições dos balões da app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu adicional"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Adicionar novamente à pilha"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> do <xliff:g id="APP_NAME">%2$s</xliff:g> e mais<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>."</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover p/ parte sup. esquerda"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover parte superior direita"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover p/ parte infer. esquerda"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover parte inferior direita"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Definições de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar balão"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não apresentar a conversa em balões"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse no chat através de balões"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"As novas conversas aparecem como ícones flutuantes ou balões. Toque para abrir o balão. Arraste para o mover."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controle os balões em qualquer altura"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toque em Gerir para desativar os balões desta app."</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e ignorados vão aparecer aqui."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
+</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
new file mode 100644
index 000000000000..9372e0f637cb
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Ecrã no ecrã"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Sem título do programa)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Fechar PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Ecrã inteiro"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
new file mode 100644
index 000000000000..465d2d17a5e7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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_settings" msgid="5468987116750491918">"Configurações"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Reproduzir"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausar"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"O app não é compatível com a inicialização em telas secundárias."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divisor de tela"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Lado esquerdo em tela cheia"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Esquerda a 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Esquerda a 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Esquerda a 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Lado direito em tela cheia"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Parte superior em tela cheia"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Parte superior a 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Parte superior a 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Parte superior a 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Parte inferior em tela cheia"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo para uma mão"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo para uma mão"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo para uma mão"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configurações de balões do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu flutuante"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Devolver à pilha"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de <xliff:g id="APP_NAME">%2$s</xliff:g> mais <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mover para canto superior esquerdo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse usando balões"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controle os balões a qualquer momento"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toque em \"Gerenciar\" para desativar os balões desse app"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e dispensados aparecerão aqui"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings_tv.xml b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
new file mode 100644
index 000000000000..57edcdf74cf4
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-pt/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
new file mode 100644
index 000000000000..55a437668b22
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_phone_close" msgid="5783752637260411309">"Închideți"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Extindeți"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Setări"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Redați"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Întrerupeți"</string>
+ <string name="pip_skip_to_next" msgid="8403429188794867653">"Treceți la următorul"</string>
+ <string name="pip_skip_to_prev" msgid="7172158111196394092">"Treceți la cel anterior"</string>
+ <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionați"</string>
+ <string name="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplicația nu acceptă lansare pe ecrane secundare."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Separator pentru ecranul împărțit"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Partea stângă pe ecran complet"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Partea stângă: 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Partea stângă: 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Partea stângă: 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Partea dreaptă pe ecran complet"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Partea de sus pe ecran complet"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Partea de sus: 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Partea de sus: 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Partea de sus: 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Partea de jos pe ecran complet"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Folosirea modului cu o mână"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pentru a ieși, glisați în sus din partea de jos a ecranului sau atingeți oriunde deasupra ferestrei aplicației"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Activați modul cu o mână"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Părăsiți modul cu o mână"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Setări pentru baloanele <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Suplimentar"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Adăugați înapoi în stivă"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <xliff:g id="APP_NAME">%2$s</xliff:g> și încă <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mutați în stânga sus"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mutați în dreapta sus"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mutați în stânga jos"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mutați în dreapta jos"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Setări <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Închideți balonul"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afișați conversația în balon"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat cu baloane"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Conversațiile noi apar ca pictograme flotante sau baloane. Atingeți pentru a deschide balonul. Trageți pentru a-l muta."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlați oricând baloanele"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Atingeți Gestionați pentru a dezactiva baloanele din această aplicație"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nu există baloane recente"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Baloanele recente și baloanele respinse vor apărea aici"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
new file mode 100644
index 000000000000..9438e4955b68
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
new file mode 100644
index 000000000000..8ae00d28f896
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Закрыть"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Развернуть"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Настройки"</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_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="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>
+ <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_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_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="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>\"."</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="NOTIFICATION_TITLE">%1$s</xliff:g> из приложения \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> от приложения \"<xliff:g id="APP_NAME">%2$s</xliff:g>\" и ещё <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Перенести в левый верхний угол"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перенести в правый верхний угол"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перенести в левый нижний угол"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перенести в правый нижний угол"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: настройки"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Скрыть всплывающий чат"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показывать всплывающий чат для разговора"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Всплывающие чаты"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новые разговоры будут появляться в виде плавающих значков, или всплывающих чатов. Чтобы открыть чат, нажмите на него, а чтобы переместить – перетащите."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Всплывающие чаты"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Чтобы отключить всплывающие чаты из этого приложения, нажмите \"Настроить\"."</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ОК"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нет недавних всплывающих чатов"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Здесь будут появляться недавние и скрытые всплывающие чаты."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings_tv.xml b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
new file mode 100644
index 000000000000..24785aa7e184
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ru/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(Без названия)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"\"Кадр в кадре\" – выйти"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Во весь экран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
new file mode 100644
index 000000000000..081926fd101b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"වසන්න"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"දිග හරින්න"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"සැකසීම්"</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_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="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>
+ <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_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_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="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> බුබුළු සඳහා සැකසීම්"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> වෙතින් <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> සහ තවත් <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ක්"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ඉහළ වමට ගෙන යන්න"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ඉහළ දකුණට ගෙන යන්න"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"පහළ වමට ගෙන යන්න"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"පහළ දකුණට ගෙන යන්න"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> සැකසීම්"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"බුබුලු ඉවත ලන්න"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"සංවාදය බුබුලු නොදමන්න"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"බුබුලු භාවිතයෙන් කතාබහ කරන්න"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"නව සංවාද පාවෙන අයිකන හෝ බුබුලු ලෙස දිස් වේ. බුබුල විවෘත කිරීමට තට්ටු කරන්න. එය ගෙන යාමට අදින්න."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ඕනෑම වේලාවක බුබුලු පාලනය කරන්න"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"මෙම යෙදුමෙන් බුබුලු ක්‍රියාවිරහිත කිරීමට කළමනාකරණය කරන්න තට්ටු කරන්න"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"තේරුණා"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"මෑත බුබුලු නැත"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"මෑත බුබුලු සහ ඉවත ලූ බුබුලු මෙහි දිස් වනු ඇත"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings_tv.xml b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
new file mode 100644
index 000000000000..62ee6d4f44d2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-si/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(මාතෘකාවක් නැති වැඩසටහන)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP වසන්න"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"සම්පූර්ණ තිරය"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
new file mode 100644
index 000000000000..24fded7ebb04
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Zavrieť"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Rozbaliť"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Nastavenia"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Prehrať"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pozastaviť"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikácia nepodporuje spúšťanie na sekundárnych obrazovkách."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Rozdeľovač obrazovky"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ľavá – na celú obrazovku"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Ľavá – 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Ľavá – 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Ľavá – 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Pravá– na celú obrazovku"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Horná – na celú obrazovku"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Horná – 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Horná – 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Horná – 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Dolná – na celú obrazovku"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Používanie režimu jednej ruky"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Ukončíte potiahnutím z dolnej časti obrazovky nahor alebo klepnutím kdekoľvek nad aplikáciu"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Spustiť režim jednej ruky"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Ukončiť režim jednej ruky"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Nastavenia bublín aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Rozšírená ponuka"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Pridať späť do zásobníka"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikácie <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> z aplikácie <xliff:g id="APP_NAME">%2$s</xliff:g> a ďalšie (<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>)"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Presunúť doľava nahor"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Presunúť doprava nahor"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Presunúť doľava nadol"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Presunúť doprava nadol"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavenia aplikácie <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavrieť bublinu"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovať konverzáciu ako bublinu"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Čet pomocou bublín"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nové konverzácie sa zobrazujú ako plávajúce ikony či bubliny. Bublinu otvoríte klepnutím. Premiestnite ju presunutím."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Nastavenie bublín môžete kedykoľvek zmeniť"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Bubliny pre túto aplikáciu môžete vypnúť klepnutím na Spravovať"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Dobre"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žiadne nedávne bubliny"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tu sa budú zobrazovať nedávne a zavreté bubliny"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings_tv.xml b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
new file mode 100644
index 000000000000..a7a515cdc61c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sk/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Obraz v obraze"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program bez názvu)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Zavrieť režim PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Celá obrazovka"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
new file mode 100644
index 000000000000..3f425302a5ac
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Zapri"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Razširi"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Nastavitve"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Predvajaj"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Začasno ustavi"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacija ne podpira zagona na sekundarnih zaslonih."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Razdelilnik zaslonov"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Levi v celozaslonski način"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Levi 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Levi 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Levi 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Desni v celozaslonski način"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Zgornji v celozaslonski način"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Zgornji 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Zgornji 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Zgornji 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Spodnji v celozaslonski način"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Uporaba enoročnega načina"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Za izhod povlecite z dna zaslona navzgor ali se dotaknite na poljubnem mestu nad aplikacijo"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Zagon enoročnega načina"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Izhod iz enoročnega načina"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Nastavitve za oblačke aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Prelivanje"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Dodaj nazaj v sklad"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> (<xliff:g id="APP_NAME">%2$s</xliff:g>)"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iz aplikacije <xliff:g id="APP_NAME">%2$s</xliff:g> in toliko drugih: <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Premakni zgoraj levo"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premakni zgoraj desno"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premakni spodaj levo"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premakni spodaj desno"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavitve za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Opusti oblaček"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Pogovora ne prikaži v oblačku"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Klepet z oblački"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi pogovori so prikazani kot lebdeče ikone ali oblački. Če želite odpreti oblaček, se ga dotaknite. Če ga želite premakniti, ga povlecite."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Upravljanje oblačkov"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Dotaknite se »Upravljanje«, da izklopite oblačke iz te aplikacije"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"V redu"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ni nedavnih oblačkov"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tukaj bodo prikazani tako nedavni kot tudi opuščeni oblački"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings_tv.xml b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
new file mode 100644
index 000000000000..fe5c9ae5d2a8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sl/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Slika v sliki"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program brez naslova)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Zapri način PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Celozaslonsko"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
new file mode 100644
index 000000000000..ddae724e7569
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Mbyll"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Zgjero"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Cilësimet"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Luaj"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Ndërprit"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Aplikacioni nuk mbështet nisjen në ekrane dytësore."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Ndarësi i ekranit të ndarë"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Ekrani i plotë majtas"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Majtas 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Majtas 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Majtas 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Ekrani i plotë djathtas"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Ekrani i plotë lart"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Lart 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Lart 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Lart 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ekrani i plotë poshtë"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Po përdor modalitetin e përdorimit me një dorë"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Për të dalë, rrëshqit lart nga fundi i ekranit ose trokit diku mbi aplikacion"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Modaliteti i përdorimit me një dorë"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Dil nga modaliteti i përdorimit me një dorë"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Cilësimet për flluskat e <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Tejkalo"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Shto përsëri te stiva"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> nga <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> nga <xliff:g id="APP_NAME">%2$s</xliff:g> dhe <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> të tjera"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Zhvendos lart majtas"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Lëviz lart djathtas"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Zhvendos poshtë majtas"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Lëvize poshtë djathtas"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Cilësimet e <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Hiqe flluskën"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Mos e vendos bisedën në flluskë"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bisedo duke përdorur flluskat"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Bisedat e reja shfaqen si ikona pluskuese ose flluska. Trokit për të hapur flluskën. Zvarrit për ta zhvendosur."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrollo flluskat në çdo moment"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Trokit \"Menaxho\" për të çaktivizuar flluskat nga ky aplikacion"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"E kuptova"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nuk ka flluska të fundit"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Flluskat e fundit dhe flluskat e hequra do të shfaqen këtu"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings_tv.xml b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
new file mode 100644
index 000000000000..1d5583b2c826
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sq/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Figurë brenda figurës"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program pa titull)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Mbyll PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Ekrani i plotë"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
new file mode 100644
index 000000000000..74c9ac0867e3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Затвори"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Прошири"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Подешавања"</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_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="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>
+ <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_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_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="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> облачиће"</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="NOTIFICATION_TITLE">%1$s</xliff:g> из апликације <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> из апликације <xliff:g id="APP_NAME">%2$s</xliff:g> и још <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Премести горе лево"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести доле лево"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести доле десно"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Подешавања за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Одбаци облачић"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не користи облачиће за конверзацију"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Ћаскајте у облачићима"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Нове конверзације се приказују као плутајуће иконе или облачићи. Додирните да бисте отворили облачић. Превуците да бисте га преместили."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Контролишите облачиће у било ком тренутку"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Додирните Управљајте да бисте искључили облачиће из ове апликације"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Важи"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема недавних облачића"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Овде се приказују недавни и одбачени облачићи"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings_tv.xml b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
new file mode 100644
index 000000000000..62ad1e8f6e69
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sr/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(Програм без наслова)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Затвори PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Цео екран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
new file mode 100644
index 000000000000..81328a836345
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"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_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>
+ <string name="pip_play" msgid="3496151081459417097">"Spela upp"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pausa"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Appen kan inte köras på en sekundär skärm."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Avdelare för delad skärm"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Helskärm på vänster skärm"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Vänster 70 %"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Vänster 50 %"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Vänster 30 %"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Helskärm på höger skärm"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Helskärm på övre skärm"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Övre 70 %"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Övre 50 %"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Övre 30 %"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Helskärm på nedre skärm"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Använda enhandsläge"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Avsluta genom att svepa uppåt från skärmens nederkant eller trycka ovanför appen"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Starta enhandsläge"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Avsluta enhandsläge"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Inställningar för <xliff:g id="APP_NAME">%1$s</xliff:g>-bubblor"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Fler menyalternativ"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Lägg tillbaka på stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> från <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> från <xliff:g id="APP_NAME">%2$s</xliff:g> och <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> fler"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Flytta högst upp till vänster"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytta högst upp till höger"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytta längst ned till vänster"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytta längst ned till höger"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Inställningar för <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Stäng bubbla"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Visa inte konversationen i bubblor"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta med bubblor"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nya konversationer visas som flytande ikoner, så kallade bubblor. Tryck på bubblan om du vill öppna den. Dra den om du vill flytta den."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Styr bubblor när som helst"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tryck på Hantera för att stänga av bubblor från den här appen"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Inga nya bubblor"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"De senaste bubblorna och ignorerade bubblor visas här"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings_tv.xml b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
new file mode 100644
index 000000000000..74fb590c3e4d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sv/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Bild-i-bild"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Namnlöst program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Stäng PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Helskärm"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
new file mode 100644
index 000000000000..4559832b1d85
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Funga"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Panua"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Mipangilio"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Cheza"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Sitisha"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Programu hii haiwezi kufunguliwa kwenye madirisha mengine."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Kitenganishi cha skrini inayogawanywa"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Skrini nzima ya kushoto"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kushoto 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kushoto 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kushoto 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Skrini nzima ya kulia"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Skrini nzima ya juu"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Juu 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Juu 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Juu 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Skrini nzima ya chini"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Kutumia hali ya kutumia kwa mkono mmoja"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Ili ufunge, telezesha kidole juu kutoka sehemu ya chini ya skrini au uguse mahali popote juu ya programu"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Anzisha hali ya kutumia kwa mkono mmoja"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Funga hali ya kutumia kwa mkono mmoja"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Mipangilio ya viputo vya <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Vipengee vya ziada"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Rejesha kwenye rafu"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kutoka kwa <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kutoka kwa <xliff:g id="APP_NAME">%2$s</xliff:g> na nyingine<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Sogeza juu kushoto"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sogeza juu kulia"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sogeza chini kushoto"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sogeza chini kulia"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Mipangilio ya <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ondoa kiputo"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Usiweke viputo kwenye mazungumzo"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Piga gumzo ukitumia viputo"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Mazungumzo mapya huonekena kama aikoni au viputo vinavyoelea. Gusa ili ufungue kiputo. Buruta ili ukisogeze."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Dhibiti viputo wakati wowote"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Gusa Dhibiti ili uzime viputo kwenye programu hii"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Nimeelewa"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hakuna viputo vya hivi majuzi"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viputo vya hivi karibuni na vile vilivyoondolewa vitaonekana hapa"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings_tv.xml b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
new file mode 100644
index 000000000000..cf0d8a9b3910
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-sw/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Pachika Picha Ndani ya Picha Nyingine"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Programu isiyo na jina)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Funga PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Skrini nzima"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
new file mode 100644
index 000000000000..586ee94a1098
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"மூடு"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"விரி"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"அமைப்புகள்"</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_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="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>
+ <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_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_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="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> குமிழ்களுக்கான அமைப்புகள்"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> மற்றும் மேலும் <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ஆப்ஸிலிருந்து வந்துள்ள அறிவிப்பு: <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"மேலே இடப்புறமாக நகர்த்து"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"மேலே வலப்புறமாக நகர்த்து"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"குமிழை அகற்று"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"உரையாடலைக் குமிழாக்காதே"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"குமிழ்களைப் பயன்படுத்தி அரட்டையடியுங்கள்"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"புதிய உரையாடல்கள் மிதக்கும் ஐகான்களாகவோ குமிழ்களாகவோ தோன்றும். குமிழைத் திறக்க தட்டவும். நகர்த்த இழுக்கவும்."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"குமிழ்களை எப்போது வேண்டுமானாலும் கட்டுப்படுத்தலாம்"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"இந்த ஆப்ஸிலிருந்து வரும் குமிழ்களை முடக்க, நிர்வகி என்பதைத் தட்டவும்"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"சரி"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"சமீபத்திய குமிழ்கள் இல்லை"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"சமீபத்திய குமிழ்களும் நிராகரிக்கப்பட்ட குமிழ்களும் இங்கே தோன்றும்"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings_tv.xml b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
new file mode 100644
index 000000000000..8bca46314e30
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ta/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(தலைப்பு இல்லை)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIPஐ மூடு"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"முழுத்திரை"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
new file mode 100644
index 000000000000..4e85b4371220
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"మూసివేయి"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"విస్తరింపజేయి"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"సెట్టింగ్‌లు"</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_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="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>
+ <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_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_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="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> బబుల్స్ సెట్టింగ్‌లు"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> నుండి <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> మరియు మరో <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ఎగువ ఎడమవైపునకు జరుపు"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ఎగువ కుడివైపునకు జరుపు"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"దిగువ ఎడమవైపునకు తరలించు"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"దిగవు కుడివైపునకు జరుపు"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> సెట్టింగ్‌లు"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"బబుల్‌ను విస్మరించు"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"సంభాషణను బబుల్ చేయవద్దు"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"బబుల్స్‌ను ఉపయోగించి చాట్ చేయండి"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"కొత్త సంభాషణలు తేలియాడే చిహ్నాలుగా లేదా బబుల్స్ లాగా కనిపిస్తాయి. బబుల్‌ని తెరవడానికి నొక్కండి. తరలించడానికి లాగండి."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"బబుల్స్‌ను ఎప్పుడైనా నియంత్రించండి"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"ఈ యాప్ నుండి వచ్చే బబుల్స్‌ను ఆఫ్ చేయడానికి మేనేజ్ బటన్‌ను ట్యాప్ చేయండి"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"అర్థమైంది"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ఇటీవలి బబుల్స్ ఏవీ లేవు"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ఇటీవలి బబుల్స్ మరియు తీసివేసిన బబుల్స్ ఇక్కడ కనిపిస్తాయి"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings_tv.xml b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
new file mode 100644
index 000000000000..47489efbc4c2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-te/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(శీర్షిక లేని ప్రోగ్రామ్)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIPని మూసివేయి"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"పూర్తి స్క్రీన్"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
new file mode 100644
index 000000000000..66c701812ce8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"ปิด"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"ขยาย"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"การตั้งค่า"</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_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="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>
+ <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_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_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="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>"</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="NOTIFICATION_TITLE">%1$s</xliff:g> จาก <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> จาก <xliff:g id="APP_NAME">%2$s</xliff:g> และอีก <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> รายการ"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"ย้ายไปด้านซ้ายบน"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ย้ายไปด้านขวาบน"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ย้ายไปด้านซ้ายล่าง"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ย้ายไปด้านขาวล่าง"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"การตั้งค่า <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"ปิดบับเบิล"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ไม่ต้องแสดงการสนทนาเป็นบับเบิล"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"แชทโดยใช้บับเบิล"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"การสนทนาใหม่ๆ จะปรากฏเป็นไอคอนแบบลอยหรือบับเบิล แตะเพื่อเปิดบับเบิล ลากเพื่อย้ายที่"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"ควบคุมบับเบิลได้ทุกเมื่อ"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"แตะ \"จัดการ\" เพื่อปิดบับเบิลจากแอปนี้"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"รับทราบ"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ไม่มีบับเบิลเมื่อเร็วๆ นี้"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"บับเบิลที่แสดงและที่ปิดไปเมื่อเร็วๆ นี้จะปรากฏที่นี่"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings_tv.xml b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
new file mode 100644
index 000000000000..d3797e7c3cde
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-th/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(ไม่มีชื่อรายการ)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"ปิด PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"เต็มหน้าจอ"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
new file mode 100644
index 000000000000..a76bf6f1350c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Isara"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Palawakin"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Mga Setting"</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>
+ <string name="pip_play" msgid="3496151081459417097">"I-play"</string>
+ <string name="pip_pause" msgid="690688849510295232">"I-pause"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Hindi sinusuportahan ng app ang paglulunsad sa mga pangalawang display."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Divider ng split-screen"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"I-full screen ang nasa kaliwa"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Gawing 70% ang nasa kaliwa"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Gawing 50% ang nasa kaliwa"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Gawing 30% ang nasa kaliwa"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"I-full screen ang nasa kanan"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"I-full screen ang nasa itaas"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Gawing 70% ang nasa itaas"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Gawing 50% ang nasa itaas"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Gawing 30% ang nasa itaas"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"I-full screen ang nasa ibaba"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Paggamit ng one-hand mode"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para lumabas, mag-swipe pataas mula sa ibaba ng screen o mag-tap kahit saan sa itaas ng app"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Simulan ang one-hand mode"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Lumabas sa one-hand mode"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Mga setting para sa mga bubble ng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overflow"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Idagdag ulit sa stack"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> mula sa <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> mula sa <xliff:g id="APP_NAME">%2$s</xliff:g> at <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> pa"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Ilipat sa kaliwa sa itaas"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ilipat sa kanan sa itaas"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ilipat sa kaliwa sa ibaba"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ilipat sa kanan sa ibaba"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Mga setting ng <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"I-dismiss ang bubble"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Huwag ipakita sa bubble ang mga pag-uusap"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Mag-chat gamit ang bubbles"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Lumalabas bilang mga nakalutang na icon o bubble ang mga bagong pag-uusap. I-tap para buksan ang bubble. I-drag para ilipat ito."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kontrolin ang mga bubble anumang oras"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"I-tap ang Pamahalaan para i-off ang mga bubble mula sa app na ito"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Walang kamakailang bubble"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Lalabas dito ang mga kamakailang bubble at na-dismiss na bubble"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings_tv.xml b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
new file mode 100644
index 000000000000..b01c1115cd34
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-tl/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-Picture"</string>
+ <string name="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>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
new file mode 100644
index 000000000000..b3276dad50e7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Kapat"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Genişlet"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Ayarlar"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Oynat"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Duraklat"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Uygulama ikincil ekranlarda başlatılmayı desteklemiyor."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Bölünmüş ekran ayırıcı"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Solda tam ekran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Solda %70"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Solda %50"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Solda %30"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Sağda tam ekran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Üstte tam ekran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Üstte %70"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Üstte %50"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Üstte %30"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Altta tam ekran"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Tek el modunu kullanma"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Çıkmak için ekranın alt kısmından yukarı kaydırın veya uygulamanın üzerinde herhangi bir yere dokunun"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Tek el modunu başlat"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Tek el modundan çık"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> baloncukları için ayarlar"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Taşma"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Yığına geri ekle"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> uygulamasından <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> uygulamasından <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ve diğer <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Sol üste taşı"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sağ üste taşı"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sol alta taşı"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sağ alta taşı"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Baloncuğu kapat"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Görüşmeyi baloncuk olarak görüntüleme"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Baloncukları kullanarak sohbet edin"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Yeni görüşmeler kayan simgeler veya baloncuk olarak görünür. Açmak için baloncuğa dokunun. Baloncuğu taşımak için sürükleyin."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Baloncukları istediğiniz zaman kontrol edin"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Bu uygulamanın baloncuklarını kapatmak için Yönet\'e dokunun"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Anladım"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Son kapatılan baloncuk yok"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son baloncuklar ve kapattığınız baloncuklar burada görünür"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings_tv.xml b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
new file mode 100644
index 000000000000..c92c4d02f465
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-tr/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Pencere İçinde Pencere"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Başlıksız program)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"PIP\'yi kapat"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Tam ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
new file mode 100644
index 000000000000..8e303cf45a39
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Закрити"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Розгорнути"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Налаштування"</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_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="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>
+ <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_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_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="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>"</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">"Cповіщення \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" від додатка <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"Сповіщення \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\" від додатка <xliff:g id="APP_NAME">%2$s</xliff:g> (і ще <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>)"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Перемістити ліворуч угору"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перемістити праворуч угору"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перемістити ліворуч униз"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перемістити праворуч униз"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Налаштування параметра \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Закрити підказку"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показувати спливаючі чати для розмов"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Спливаючий чат"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Нові повідомлення чату з\'являються у вигляді спливаючих значків. Щоб відкрити чат, натисніть його, а щоб перемістити – перетягніть."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Контроль спливаючих чатів"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Натисніть \"Налаштувати\", щоб вимкнути спливаючі чати від цього додатка"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Зрозуміло"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Немає нещодавніх спливаючих чатів"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Тут з\'являтимуться нещодавні й закриті спливаючі чати"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings_tv.xml b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
new file mode 100644
index 000000000000..74d4723d7850
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-uk/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(Програма без назви)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Закрити PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"На весь екран"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
new file mode 100644
index 000000000000..cd6529f0bf94
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"بند کریں"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"پھیلائیں"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"ترتیبات"</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_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>
+ <!-- no translation found for dock_forced_resizable (1749750436092293116) -->
+ <skip />
+ <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_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_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>
+ <!-- no translation found for one_handed_tutorial_title (4583241688067426350) -->
+ <skip />
+ <!-- no translation found for one_handed_tutorial_description (3486582858591353067) -->
+ <skip />
+ <!-- no translation found for accessibility_action_start_one_handed (5070337354072861426) -->
+ <skip />
+ <!-- no translation found for accessibility_action_stop_one_handed (1369940261782179442) -->
+ <skip />
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> بلبلوں کے لیے ترتیبات"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"اوورفلو"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"انبار میں واپس شامل کریں"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="APP_NAME">%2$s</xliff:g> کی جانب سے <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> اور <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> مزید سے <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"اوپر بائیں جانب لے جائیں"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"اوپر دائیں جانب لے جائيں"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"بلبلہ برخاست کریں"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"گفتگو بلبلہ نہ کریں"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"بلبلے کے ذریعے چیٹ کریں"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"نئی گفتگوئیں فلوٹنگ آئیکن یا بلبلے کے طور پر ظاہر ہوں گی۔ بلبلہ کھولنے کے لیے تھپتھپائیں۔ اسے منتقل کرنے کے لیے گھسیٹیں۔"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کسی بھی وقت بلبلے کو کنٹرول کریں"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"اس ایپ سے بلبلوں کو آف کرنے کے لیے نظم کریں پر تھپتھپائیں"</string>
+ <!-- no translation found for bubbles_user_education_got_it (3382046149225428296) -->
+ <skip />
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"کوئی حالیہ بلبلہ نہیں"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حالیہ بلبلے اور برخاست شدہ بلبلے یہاں ظاہر ہوں گے"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings_tv.xml b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
new file mode 100644
index 000000000000..64e5db5ae10a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-ur/strings_tv.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for notification_channel_tv_pip (2576686079160402435) -->
+ <skip />
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(بلا عنوان پروگرام)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"‏PIP بند کریں"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"فُل اسکرین"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
new file mode 100644
index 000000000000..795eff8d2e07
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Yopish"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Yoyish"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Sozlamalar"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Ijro"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Pauza"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Bu ilova qo‘shimcha ekranlarda ishga tushmaydi."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Ekranni ikkiga bo‘lish chizig‘i"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Chapda to‘liq ekran"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Chapda 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Chapda 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Chapda 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"O‘ngda to‘liq ekran"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Tepada to‘liq ekran"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Tepada 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Tepada 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Tepada 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pastda to‘liq ekran"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Ixcham rejimdan foydalanish"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Chiqish uchun ekran pastidan tepaga suring yoki ilovaning tepasidagi istalgan joyga bosing."</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Ixcham rejimni ishga tushirish"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Ixcham rejimdan chiqish"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> bulutchalari uchun sozlamalar"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Kengaytirilgan"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Yana toʻplamga kiritish"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g> ilovasidan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> va yana <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ta bildirishnoma"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Yuqori chapga surish"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuqori oʻngga surish"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Quyi chapga surish"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Quyi oʻngga surish"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> sozlamalari"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bulutchani yopish"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Suhbatlar bulutchalar shaklida chiqmasin"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bulutchalar yordamida subhatlashish"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Yangi xabarlar qalqib chiquvchi belgilar yoki bulutchalar kabi chiqadi. Xabarni ochish uchun bildirishnoma ustiga bosing. Xabarni qayta joylash uchun bildirishnomani suring."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bulutchalardagi bildirishnomalar"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Bu ilova bulutchalarini faolsizlantirish uchun Boshqarish tugmasini bosing"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Avvalgi bulutchalar topilmadi"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Bu yerda oxirgi va yopilgan bulutcha shaklidagi bildirishnomalar chiqadi"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings_tv.xml b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
new file mode 100644
index 000000000000..ae5a647301c8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-uz/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Tasvir ustida tasvir"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Nomsiz)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Kadr ichida kadr – chiqish"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Butun ekran"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
new file mode 100644
index 000000000000..ce372317b0b8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Đó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_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>
+ <string name="pip_play" msgid="3496151081459417097">"Phát"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Tạm dừng"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Ứng dụng không hỗ trợ khởi chạy trên màn hình phụ."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Bộ chia chia đôi màn hình"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Toàn màn hình bên trái"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Trái 70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Trái 50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Trái 30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Toàn màn hình bên phải"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Toàn màn hình phía trên"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Trên 70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Trên 50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Trên 30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Toàn màn hình phía dưới"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Cách dùng chế độ một tay"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Để thoát, hãy vuốt lên từ cuối màn hình hoặc nhấn vào vị trí bất kỳ phía trên ứng dụng"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bắt đầu chế độ một tay"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Thoát khỏi chế độ một tay"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Tùy chọn cài đặt cho bong bóng trò chuyện <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Trình đơn mục bổ sung"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Thêm lại vào ngăn xếp"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> của <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> từ <xliff:g id="APP_NAME">%2$s</xliff:g> và <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> bong bóng khác"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Chuyển lên trên cùng bên trái"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Chuyển lên trên cùng bên phải"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Chuyển tới dưới cùng bên trái"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Chuyển tới dưới cùng bên phải"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"Cài đặt <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Đóng bong bóng"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Dừng sử dụng bong bóng cho cuộc trò chuyện"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Trò chuyện bằng bong bóng trò chuyện"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Các cuộc trò chuyện mới sẽ xuất hiện dưới dạng biểu tượng nổi hoặc bong bóng trò chuyện. Nhấn để mở bong bóng trò chuyện. Kéo để di chuyển bong bóng trò chuyện."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Kiểm soát bong bóng bất cứ lúc nào"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Nhấn vào nút Quản lý để tắt bong bóng trò chuyện từ ứng dụng này"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Đã hiểu"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Không có bong bóng trò chuyện nào gần đây"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Bong bóng trò chuyện đã đóng và bong bóng trò chuyện gần đây sẽ xuất hiện ở đây"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings_tv.xml b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
new file mode 100644
index 000000000000..082d12596076
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-vi/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Hình trong hình"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Không có chương trình tiêu đề)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Đóng PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Toàn màn hì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
new file mode 100644
index 000000000000..3143130fa4ce
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"关闭"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"展开"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"设置"</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_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="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>
+ <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_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_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="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>对话泡的设置"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="APP_NAME">%2$s</xliff:g>和另外 <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> 个应用:<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"移至左上角"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上角"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"关闭对话泡"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不以对话泡形式显示对话"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"使用对话泡聊天"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"新对话会以浮动图标或对话泡形式显示。点按即可打开对话泡。拖动即可移动对话泡。"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"随时控制对话泡"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"点按“管理”按钮,可关闭来自此应用的对话泡"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"知道了"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近没有对话泡"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"此处会显示最近的对话泡和已关闭的对话泡"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
+</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
new file mode 100644
index 000000000000..cb3fcf7c4c16
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(节目没有标题)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"关闭画中画"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"全屏"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..4f8bfe016f6f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"關閉"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"設定"</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_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="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>
+ <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_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_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="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>」小視窗設定"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"來自「<xliff:g id="APP_NAME">%2$s</xliff:g>」及另外 <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> 個應用程式的<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"移去左上角"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移去右上角"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"關閉小視窗氣泡"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要透過小視窗顯示對話"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"使用小視窗進行即時通訊"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"新對話會以浮動圖示 (小視窗) 顯示。輕按即可開啟小視窗。拖曳即可移動小視窗。"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"隨時控制小視窗設定"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"輕按「管理」即可關閉此應用程式的小視窗"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"知道了"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"沒有最近曾使用的小視窗"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近使用和關閉的小視窗會在這裡顯示"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
+</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
new file mode 100644
index 000000000000..956243ed6e6d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(沒有標題的節目)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"關閉 PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..6fb8ed963ba7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"關閉"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"設定"</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_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="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>
+ <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_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_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="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>」對話框的設定"</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>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"「<xliff:g id="APP_NAME">%2$s</xliff:g>」和其他 <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> 個應用程式:<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"移至左上方"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上方"</string>
+ <string name="bubble_accessibility_action_move_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="bubble_dismiss_text" msgid="8816558050659478158">"關閉對話框"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要以對話框形式顯示對話"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"透過對話框來聊天"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"新的對話會以浮動圖示或對話框形式顯示。輕觸即可開啟對話框,拖曳則可移動對話框。"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"你隨時可以控管對話框的各項設定"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"輕觸 [管理] 即可關閉來自這個應用程式的對話框"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"我知道了"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近沒有任何對話框"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近的對話框和已關閉的對話框會顯示在這裡"</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
+</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
new file mode 100644
index 000000000000..08b2f4bbca89
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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="pip_notification_unknown_title" msgid="2729870284350772311">"(無標題的節目)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"關閉子母畫面"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"全螢幕"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
new file mode 100644
index 000000000000..cab277647d26
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Vala"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Nweba"</string>
+ <string name="pip_phone_settings" msgid="5468987116750491918">"Izilungiselelo"</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>
+ <string name="pip_play" msgid="3496151081459417097">"Dlala"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Misa isikhashana"</string>
+ <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="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>
+ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"Uhlelo lokusebenza alusekeli ukuqalisa kuzibonisi zesibili."</string>
+ <string name="accessibility_divider" msgid="703810061635792791">"Isihlukanisi sokuhlukanisa isikrini"</string>
+ <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"Isikrini esigcwele esingakwesokunxele"</string>
+ <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"Kwesokunxele ngo-70%"</string>
+ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"Kwesokunxele ngo-50%"</string>
+ <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"Kwesokunxele ngo-30%"</string>
+ <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"Isikrini esigcwele esingakwesokudla"</string>
+ <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"Isikrini esigcwele esiphezulu"</string>
+ <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"Okuphezulu okungu-70%"</string>
+ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Okuphezulu okungu-50%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Okuphezulu okungu-30%"</string>
+ <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ngaphansi kwesikrini esigcwele"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Ukusebenzisa imodi yesandla esisodwa"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Ukuze uphume, swayipha ngaphezulu kusuka ngezansi kwesikrini noma thepha noma kuphi ngenhla kohlelo lokusebenza"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Qalisa imodi yesandla esisodwa"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Phuma kumodi yesandla esisodwa"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Izilungiselelo zamabhamuza e-<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Ukuphuphuma"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Engeza emuva kusitaki"</string>
+ <string name="bubble_content_description_single" msgid="8495748092720065813">"I-<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kusuka ku-<xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+ <string name="bubble_content_description_stack" msgid="8071515017164630429">"I-<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> kusukela ku-<xliff:g id="APP_NAME">%2$s</xliff:g> nokungu-<xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> ngaphezulu"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Hambisa phezulu kwesokunxele"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Hambisa phezulu ngakwesokudla"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Hambisa inkinobho ngakwesokunxele"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Hambisa inkinobho ngakwesokudla"</string>
+ <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> izilungiselelo"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cashisa ibhamuza"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ungayibhamuzi ingxoxo"</string>
+ <string name="bubbles_user_education_title" msgid="2112319053732691899">"Xoxa usebenzisa amabhamuza"</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Izingxoxo ezintsha zivela njengezithonjana ezintantayo, noma amabhamuza. Thepha ukuze uvule ibhamuza. Hudula ukuze ulihambise."</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Lawula amabhamuza noma nini"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Thepha okuthi Phatha ukuvala amabhamuza kusuka kulolu hlelo lokusebenza"</string>
+ <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ngiyezwa"</string>
+ <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Awekho amabhamuza akamuva"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Amabhamuza akamuva namabhamuza asusiwe azobonakala lapha."</string>
+ <string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
+ <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings_tv.xml b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
new file mode 100644
index 000000000000..89c7f498652d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/values-zu/strings_tv.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources 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">"Isithombe-esithombeni"</string>
+ <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Alukho uhlelo lwesihloko)"</string>
+ <string name="pip_close" msgid="9135220303720555525">"Vala i-PIP"</string>
+ <string name="pip_fullscreen" msgid="7278047353591302554">"Iskrini esigcwele"</string>
+</resources>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index e99350b264b9..c913e0c441e5 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -36,4 +36,16 @@
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
when the PIP menu is shown in center. -->
<string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
+
+ <!-- Gravity of letterboxed apps in portrait screen orientation.
+ Can be Gravity.TOP, Gravity.CENTER or Gravity.BOTTOM.
+ Any other value will result in runtime exception for a letterboxed activity.
+ Default is Gravity.TOP. -->
+ <integer name="config_letterboxPortraitGravity">0x00000030</integer>
+
+ <!-- Gravity of letterboxed apps in landscape screen orientation.
+ Can be Gravity.LEFT, Gravity.CENTER or Gravity.RIGHT.
+ Any other value will result in runtime exception for a letterboxed activity.
+ Default is Gravity.CENTER. -->
+ <integer name="config_letterboxLandscapeGravity">0x00000011</integer>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
index fc0a76e8d286..4f13b83bc29d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
@@ -32,14 +32,17 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.io.PrintWriter;
-class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
+/**
+ * Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}.
+ */
+public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FullscreenTaskListener";
private final SyncTransactionQueue mSyncQueue;
private final ArraySet<Integer> mTasks = new ArraySet<>();
- FullscreenTaskListener(SyncTransactionQueue syncQueue) {
+ public FullscreenTaskListener(SyncTransactionQueue syncQueue) {
mSyncQueue = syncQueue;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/LetterboxTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/LetterboxTaskListener.java
deleted file mode 100644
index 9010c2088c34..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/LetterboxTaskListener.java
+++ /dev/null
@@ -1,110 +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;
-
-import android.app.ActivityManager;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.SurfaceControl;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
-
-/**
- * Organizes a task in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN} when
- * it's presented in the letterbox mode either because orientations of a top activity and a device
- * don't match or because a top activity is in a size compat mode.
- */
-final class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener {
- private static final String TAG = "LetterboxTaskListener";
-
- private final SyncTransactionQueue mSyncQueue;
-
- private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>();
-
- LetterboxTaskListener(SyncTransactionQueue syncQueue) {
- mSyncQueue = syncQueue;
- }
-
- @Override
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- synchronized (mLeashByTaskId) {
- if (mLeashByTaskId.get(taskInfo.taskId) != null) {
- throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
- }
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Appeared: #%d",
- taskInfo.taskId);
- mLeashByTaskId.put(taskInfo.taskId, leash);
- final Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
- final Rect activtyBounds = taskInfo.letterboxActivityBounds;
- final Point taskPositionInParent = taskInfo.positionInParent;
- mSyncQueue.runInSync(t -> {
- setPositionAndWindowCrop(
- t, leash, activtyBounds, taskBounds, taskPositionInParent);
- if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
- t.setAlpha(leash, 1f);
- t.setMatrix(leash, 1, 0, 0, 1);
- t.show(leash);
- }
- });
- }
- }
-
- @Override
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- synchronized (mLeashByTaskId) {
- if (mLeashByTaskId.get(taskInfo.taskId) == null) {
- Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
- return;
- }
- mLeashByTaskId.remove(taskInfo.taskId);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Vanished: #%d",
- taskInfo.taskId);
- }
- }
-
- @Override
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- synchronized (mLeashByTaskId) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Changed: #%d",
- taskInfo.taskId);
- final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId);
- final Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
- final Rect activtyBounds = taskInfo.letterboxActivityBounds;
- final Point taskPositionInParent = taskInfo.positionInParent;
- mSyncQueue.runInSync(t -> {
- setPositionAndWindowCrop(
- t, leash, activtyBounds, taskBounds, taskPositionInParent);
- });
- }
- }
-
- private static void setPositionAndWindowCrop(
- SurfaceControl.Transaction transaction,
- SurfaceControl leash,
- final Rect activityBounds,
- final Rect taskBounds,
- final Point taskPositionInParent) {
- Rect activtyInTaskCoordinates = new Rect(activityBounds);
- activtyInTaskCoordinates.offset(-taskBounds.left, -taskBounds.top);
- transaction.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
- transaction.setWindowCrop(leash, activtyInTaskCoordinates);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
new file mode 100644
index 000000000000..ada7e1a9e3ab
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell;
+
+import android.view.Gravity;
+
+import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
+import com.android.wm.shell.letterbox.LetterboxConfigController;
+import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.io.PrintWriter;
+import java.util.Optional;
+
+/**
+ * An entry point into the shell for dumping shell internal state and running adb commands.
+ *
+ * Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}.
+ */
+public final class ShellCommandHandler {
+
+ private final Optional<SplitScreen> mSplitScreenOptional;
+ private final Optional<Pip> mPipOptional;
+ private final Optional<OneHanded> mOneHandedOptional;
+ private final Optional<HideDisplayCutout> mHideDisplayCutout;
+ private final ShellTaskOrganizer mShellTaskOrganizer;
+ private final Optional<AppPairs> mAppPairsOptional;
+ private final LetterboxConfigController mLetterboxConfigController;
+
+ public ShellCommandHandler(
+ ShellTaskOrganizer shellTaskOrganizer,
+ Optional<SplitScreen> splitScreenOptional,
+ Optional<Pip> pipOptional,
+ Optional<OneHanded> oneHandedOptional,
+ Optional<HideDisplayCutout> hideDisplayCutout,
+ Optional<AppPairs> appPairsOptional,
+ LetterboxConfigController letterboxConfigController) {
+ mShellTaskOrganizer = shellTaskOrganizer;
+ mSplitScreenOptional = splitScreenOptional;
+ mPipOptional = pipOptional;
+ mOneHandedOptional = oneHandedOptional;
+ mHideDisplayCutout = hideDisplayCutout;
+ mAppPairsOptional = appPairsOptional;
+ mLetterboxConfigController = letterboxConfigController;
+ }
+
+ /** Dumps WM Shell internal state. */
+ public void dump(PrintWriter pw) {
+ mShellTaskOrganizer.dump(pw, "");
+ pw.println();
+ pw.println();
+ mPipOptional.ifPresent(pip -> pip.dump(pw));
+ mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
+ mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
+ mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
+ pw.println();
+ pw.println();
+ mAppPairsOptional.ifPresent(appPairs -> appPairs.dump(pw, ""));
+ }
+
+
+ /** Returns {@code true} if command was found and executed. */
+ public boolean handleCommand(String[] args, PrintWriter pw) {
+ if (args.length < 2) {
+ // Argument at position 0 is "WMShell".
+ return false;
+ }
+ switch (args[1]) {
+ case "set-letterbox-portrait-gravity":
+ return runSetLetterboxPortraitGravity(args, pw);
+ case "get-letterbox-portrait-gravity":
+ return runGetLetterboxPortraitGravity(pw);
+ case "set-letterbox-landscape-gravity":
+ return runSetLetterboxLandscapeGravity(args, pw);
+ case "get-letterbox-landscape-gravity":
+ return runGetLetterboxLandscapeGravity(pw);
+ case "pair":
+ return runPair(args, pw);
+ case "unpair":
+ return runUnpair(args, pw);
+ case "help":
+ return runHelp(pw);
+ default:
+ return false;
+ }
+ }
+
+ private boolean runSetLetterboxPortraitGravity(String[] args, PrintWriter pw) {
+ if (args.length < 3) {
+ // First two arguments are "WMShell" and command name.
+ pw.println("Error: reset, TOP, CENTER or BOTTOM should be provided as an argument");
+ return true;
+ }
+ switch (args[2]) {
+ case "reset":
+ mLetterboxConfigController.resetPortraitGravity();
+ break;
+ case "TOP":
+ mLetterboxConfigController.setPortraitGravity(Gravity.TOP);
+ break;
+ case "CENTER":
+ mLetterboxConfigController.setPortraitGravity(Gravity.CENTER);
+ break;
+ case "BOTTOM":
+ mLetterboxConfigController.setPortraitGravity(Gravity.BOTTOM);
+ break;
+ default:
+ pw.println("Error: expected reset, TOP, CENTER or BOTTOM but got " + args[2]);
+ }
+ return true;
+ }
+
+ private boolean runGetLetterboxPortraitGravity(PrintWriter pw) {
+ final int gravity = mLetterboxConfigController.getPortraitGravity();
+ switch (gravity) {
+ case Gravity.TOP:
+ pw.println("TOP");
+ break;
+ case Gravity.CENTER:
+ pw.println("CENTER");
+ break;
+ case Gravity.BOTTOM:
+ pw.println("BOTTOM");
+ break;
+ default:
+ throw new AssertionError("Unexpected gravity: " + gravity);
+ }
+ return true;
+ }
+
+ private boolean runSetLetterboxLandscapeGravity(String[] args, PrintWriter pw) {
+ if (args.length < 3) {
+ // First two arguments are "WMShell" and command name.
+ pw.println("Error: reset, LEFT, CENTER or RIGHT should be provided as an argument");
+ return false;
+ }
+ switch (args[2]) {
+ case "reset":
+ mLetterboxConfigController.resetLandscapeGravity();
+ break;
+ case "LEFT":
+ mLetterboxConfigController.setLandscapeGravity(Gravity.LEFT);
+ break;
+ case "CENTER":
+ mLetterboxConfigController.setLandscapeGravity(Gravity.CENTER);
+ break;
+ case "RIGHT":
+ mLetterboxConfigController.setLandscapeGravity(Gravity.RIGHT);
+ break;
+ default:
+ pw.println(
+ "Error: expected reset, LEFT, CENTER or RIGHT but got " + args[2]);
+ }
+ return true;
+ }
+
+ private boolean runGetLetterboxLandscapeGravity(PrintWriter pw) {
+ final int gravity = mLetterboxConfigController.getLandscapeGravity();
+ switch (gravity) {
+ case Gravity.LEFT:
+ pw.println("LEFT");
+ break;
+ case Gravity.CENTER:
+ pw.println("CENTER");
+ break;
+ case Gravity.RIGHT:
+ pw.println("RIGHT");
+ break;
+ default:
+ throw new AssertionError("Unexpected gravity: " + gravity);
+ }
+ return true;
+ }
+
+ private boolean runPair(String[] args, PrintWriter pw) {
+ if (args.length < 4) {
+ // First two arguments are "WMShell" and command name.
+ pw.println("Error: two task ids should be provided as arguments");
+ return false;
+ }
+ final int taskId1 = new Integer(args[2]);
+ final int taskId2 = new Integer(args[3]);
+ mAppPairsOptional.ifPresent(appPairs -> appPairs.pair(taskId1, taskId2));
+ return true;
+ }
+
+ private boolean runUnpair(String[] args, PrintWriter pw) {
+ if (args.length < 3) {
+ // First two arguments are "WMShell" and command name.
+ pw.println("Error: task id should be provided as an argument");
+ return false;
+ }
+ final int taskId = new Integer(args[2]);
+ mAppPairsOptional.ifPresent(appPairs -> appPairs.unpair(taskId));
+ return true;
+ }
+
+ private boolean runHelp(PrintWriter pw) {
+ pw.println("Window Manager Shell commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" <no arguments provided>");
+ pw.println(" Dump Window Manager Shell internal state");
+ pw.println(" set-letterbox-portrait-gravity [reset|TOP|CENTER|BOTTOM]");
+ pw.println(" get-letterbox-portrait-gravity");
+ pw.println(" Set, reset or print letterbox gravity for portrait screen mode.");
+ pw.println(" set-letterbox-landscape-gravity [reset|LEFT|CENTER|RIGHT]");
+ pw.println(" get-letterbox-landscape-gravity");
+ pw.println(" Set, reset or print letterbox gravity for landscape screen mode.");
+ pw.println(" pair <taskId1> <taskId2>");
+ pw.println(" unpair <taskId>");
+ pw.println(" Pairs/unpairs tasks with given ids.");
+ return true;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java
deleted file mode 100644
index bb9accd78bab..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellDump.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell;
-
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
-import com.android.wm.shell.onehanded.OneHanded;
-import com.android.wm.shell.pip.Pip;
-import com.android.wm.shell.splitscreen.SplitScreen;
-
-import java.io.PrintWriter;
-import java.util.Optional;
-
-/**
- * An entry point into the shell for dumping shell internal state.
- */
-public class ShellDump {
-
- private final Optional<SplitScreen> mSplitScreenOptional;
- private final Optional<Pip> mPipOptional;
- private final Optional<OneHanded> mOneHandedOptional;
- private final Optional<HideDisplayCutout> mHideDisplayCutout;
- private final ShellTaskOrganizer mShellTaskOrganizer;
-
- public ShellDump(ShellTaskOrganizer shellTaskOrganizer,
- Optional<SplitScreen> splitScreenOptional,
- Optional<Pip> pipOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutout) {
- mShellTaskOrganizer = shellTaskOrganizer;
- mSplitScreenOptional = splitScreenOptional;
- mPipOptional = pipOptional;
- mOneHandedOptional = oneHandedOptional;
- mHideDisplayCutout = hideDisplayCutout;
- }
-
- public void dump(PrintWriter pw) {
- mShellTaskOrganizer.dump(pw, "");
- pw.println();
- pw.println();
- mPipOptional.ifPresent(pip -> pip.dump(pw));
- mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
- mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
- mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
index 4269a905a0fe..118f189866eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
@@ -16,8 +16,13 @@
package com.android.wm.shell;
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_LETTERBOX;
+
+import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.letterbox.LetterboxTaskListener;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.util.Optional;
@@ -31,22 +36,38 @@ public class ShellInit {
private final DragAndDropController mDragAndDropController;
private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<SplitScreen> mSplitScreenOptional;
+ private final Optional<AppPairs> mAppPairsOptional;
+ private final LetterboxTaskListener mLetterboxTaskListener;
+ private final FullscreenTaskListener mFullscreenTaskListener;
public ShellInit(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<SplitScreen> splitScreenOptional) {
+ Optional<SplitScreen> splitScreenOptional,
+ Optional<AppPairs> appPairsOptional,
+ LetterboxTaskListener letterboxTaskListener,
+ FullscreenTaskListener fullscreenTaskListener) {
mDisplayImeController = displayImeController;
mDragAndDropController = dragAndDropController;
mShellTaskOrganizer = shellTaskOrganizer;
mSplitScreenOptional = splitScreenOptional;
+ mAppPairsOptional = appPairsOptional;
+ mLetterboxTaskListener = letterboxTaskListener;
+ mFullscreenTaskListener = fullscreenTaskListener;
}
public void init() {
// Start listening for display changes
mDisplayImeController.startMonitorDisplays();
+
+ mShellTaskOrganizer.addListenerForType(
+ mLetterboxTaskListener, TASK_LISTENER_TYPE_LETTERBOX);
+ mShellTaskOrganizer.addListenerForType(
+ mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN);
// Register the shell organizer
mShellTaskOrganizer.registerOrganizer();
+
+ mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered);
// Bind the splitscreen impl to the drag drop controller
mDragAndDropController.setSplitScreenController(mSplitScreenOptional);
}
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 ece063cabf7c..beb9690b5cbc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -26,7 +26,6 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG
import android.annotation.IntDef;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -39,6 +38,7 @@ import android.window.TaskAppearedInfo;
import android.window.TaskOrganizer;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -118,8 +118,6 @@ public class ShellTaskOrganizer extends TaskOrganizer {
SyncTransactionQueue syncQueue, TransactionPool transactionPool,
ShellExecutor mainExecutor, ShellExecutor animExecutor, Context context) {
super(taskOrganizerController, mainExecutor);
- addListenerForType(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN);
- addListenerForType(new LetterboxTaskListener(syncQueue), TASK_LISTENER_TYPE_LETTERBOX);
mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
// TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled
@@ -309,6 +307,15 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
}
+ /** Gets running task by taskId. Returns {@code null} if no such task observed. */
+ @Nullable
+ public RunningTaskInfo getRunningTaskInfo(int taskId) {
+ synchronized (mLock) {
+ final TaskAppearedInfo info = mTasks.get(taskId);
+ return info != null ? info.getTaskInfo() : null;
+ }
+ }
+
private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash,
TaskListener oldListener, TaskListener newListener) {
if (oldListener == newListener) return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
index 04be3b70fc65..388eb28223dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
@@ -109,13 +109,10 @@ public class Transitions extends ITransitionPlayer.Stub {
mAnimExecutor.execute(va::start);
}
- private static boolean isOpeningType(@WindowManager.TransitionOldType int legacyType) {
- // TODO(shell-transitions): consider providing and using z-order vs the global type for
- // this determination.
- return legacyType == WindowManager.TRANSIT_OLD_TASK_OPEN
- || legacyType == WindowManager.TRANSIT_OLD_TASK_TO_FRONT
- || legacyType == WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND
- || legacyType == WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+ private static boolean isOpeningType(@WindowManager.TransitionType int type) {
+ return type == WindowManager.TRANSIT_OPEN
+ || type == WindowManager.TRANSIT_TO_FRONT
+ || type == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
new file mode 100644
index 000000000000..f199072f7bca
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -0,0 +1,243 @@
+/*
+ * 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.apppairs;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+
+import android.app.ActivityManager;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import java.io.PrintWriter;
+
+/**
+ * An app-pairs consisting of {@link #mRootTaskInfo} that acts as the hierarchy parent of
+ * {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
+ * Also includes all UI for managing the pair like the divider.
+ */
+class AppPair implements ShellTaskOrganizer.TaskListener {
+ private static final String TAG = AppPair.class.getSimpleName();
+
+ private ActivityManager.RunningTaskInfo mRootTaskInfo;
+ private SurfaceControl mRootTaskLeash;
+ private ActivityManager.RunningTaskInfo mTaskInfo1;
+ private SurfaceControl mTaskLeash1;
+ private ActivityManager.RunningTaskInfo mTaskInfo2;
+ private SurfaceControl mTaskLeash2;
+
+ private final AppPairsController mController;
+ private final SyncTransactionQueue mSyncQueue;
+ private final DisplayController mDisplayController;
+ private AppPairLayout mAppPairLayout;
+
+ AppPair(AppPairsController controller) {
+ mController = controller;
+ mSyncQueue = controller.getSyncTransactionQueue();
+ mDisplayController = controller.getDisplayController();
+ }
+
+ int getRootTaskId() {
+ return mRootTaskInfo != null ? mRootTaskInfo.taskId : INVALID_TASK_ID;
+ }
+
+ private int getTaskId1() {
+ return mTaskInfo1 != null ? mTaskInfo1.taskId : INVALID_TASK_ID;
+ }
+
+ private int getTaskId2() {
+ return mTaskInfo2 != null ? mTaskInfo2.taskId : INVALID_TASK_ID;
+ }
+
+ boolean contains(int taskId) {
+ return taskId == getRootTaskId() || taskId == getTaskId1() || taskId == getTaskId2();
+ }
+
+ boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
+ task1.taskId, task2.taskId, this);
+
+ if (!task1.isResizeable || !task2.isResizeable) {
+ ProtoLog.e(WM_SHELL_TASK_ORG,
+ "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
+ task1.isResizeable, task2.isResizeable);
+ return false;
+ }
+
+ mTaskInfo1 = task1;
+ mTaskInfo2 = task2;
+ mAppPairLayout = new AppPairLayout(
+ mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
+ mDisplayController.getDisplay(mRootTaskInfo.displayId),
+ mRootTaskInfo.configuration,
+ mRootTaskLeash);
+
+ final WindowContainerToken token1 = task1.token;
+ final WindowContainerToken token2 = task2.token;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ wct.setHidden(mRootTaskInfo.token, false)
+ .reparent(token1, mRootTaskInfo.token, true /* onTop */)
+ .reparent(token2, mRootTaskInfo.token, true /* onTop */)
+ .setWindowingMode(token1, WINDOWING_MODE_MULTI_WINDOW)
+ .setWindowingMode(token2, WINDOWING_MODE_MULTI_WINDOW)
+ .setBounds(token1, mAppPairLayout.getBounds1())
+ .setBounds(token2, mAppPairLayout.getBounds2())
+ // Moving the root task to top after the child tasks were repareted , or the root
+ // task cannot be visible and focused.
+ .reorder(mRootTaskInfo.token, true);
+ mController.getTaskOrganizer().applyTransaction(wct);
+ return true;
+ }
+
+ void unpair() {
+ final WindowContainerToken token1 = mTaskInfo1.token;
+ final WindowContainerToken token2 = mTaskInfo2.token;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ // Reparent out of this container and reset windowing mode.
+ wct.setHidden(mRootTaskInfo.token, true)
+ .reorder(mRootTaskInfo.token, false)
+ .reparent(token1, null, false /* onTop */)
+ .reparent(token2, null, false /* onTop */)
+ .setWindowingMode(token1, WINDOWING_MODE_UNDEFINED)
+ .setWindowingMode(token2, WINDOWING_MODE_UNDEFINED);
+ mController.getTaskOrganizer().applyTransaction(wct);
+
+ mTaskInfo1 = null;
+ mTaskInfo2 = null;
+ mAppPairLayout.release();
+ mAppPairLayout = null;
+ }
+
+ void setVisible(boolean visible) {
+ if (mAppPairLayout == null) {
+ return;
+ }
+ mAppPairLayout.setDividerVisibility(visible);
+ }
+
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ if (mRootTaskInfo == null || taskInfo.taskId == mRootTaskInfo.taskId) {
+ mRootTaskInfo = taskInfo;
+ mRootTaskLeash = leash;
+ } else if (taskInfo.taskId == getTaskId1()) {
+ mTaskInfo1 = taskInfo;
+ mTaskLeash1 = leash;
+ } else if (taskInfo.taskId == getTaskId2()) {
+ mTaskInfo2 = taskInfo;
+ mTaskLeash2 = leash;
+ } else {
+ throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
+ }
+
+ if (mTaskLeash1 == null || mTaskLeash2 == null) return;
+
+ setVisible(true);
+ final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash();
+ final Rect dividerBounds = mAppPairLayout.getDividerBounds();
+
+ // TODO: Is there more we need to do here?
+ mSyncQueue.runInSync(t -> t
+ .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x,
+ mTaskInfo1.positionInParent.y)
+ .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
+ mTaskInfo2.positionInParent.y)
+ .setLayer(dividerLeash, Integer.MAX_VALUE)
+ .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
+ .show(mRootTaskLeash)
+ .show(dividerLeash)
+ .show(mTaskLeash1)
+ .show(mTaskLeash2));
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo.taskId == getRootTaskId()) {
+ mRootTaskInfo = taskInfo;
+
+ if (mAppPairLayout != null
+ && mAppPairLayout.updateConfiguration(mRootTaskInfo.configuration)) {
+ // Update bounds when there is root bounds or orientation changed.
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash();
+ final Rect dividerBounds = mAppPairLayout.getDividerBounds();
+ final Rect bounds1 = mAppPairLayout.getBounds1();
+ final Rect bounds2 = mAppPairLayout.getBounds2();
+
+ wct.setBounds(mTaskInfo1.token, bounds1)
+ .setBounds(mTaskInfo2.token, bounds2);
+ mController.getTaskOrganizer().applyTransaction(wct);
+ mSyncQueue.runInSync(t -> t
+ .setPosition(mTaskLeash1, bounds1.left, bounds1.top)
+ .setPosition(mTaskLeash2, bounds2.left, bounds2.top)
+ .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top));
+ }
+ } else if (taskInfo.taskId == getTaskId1()) {
+ mTaskInfo1 = taskInfo;
+ } else if (taskInfo.taskId == getTaskId2()) {
+ mTaskInfo2 = taskInfo;
+ } else {
+ throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
+ }
+ }
+
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (taskInfo.taskId == getRootTaskId()) {
+ // We don't want to release this object back to the pool since the root task went away.
+ mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */);
+ } else if (taskInfo.taskId == getTaskId1() || taskInfo.taskId == getTaskId2()) {
+ mController.unpair(mRootTaskInfo.taskId);
+ }
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ final String childPrefix = innerPrefix + " ";
+ pw.println(prefix + this);
+ pw.println(innerPrefix + "Root taskId=" + getRootTaskId()
+ + " winMode=" + mRootTaskInfo.getWindowingMode());
+ if (mTaskInfo1 != null) {
+ pw.println(innerPrefix + "1 taskId=" + mTaskInfo1.taskId
+ + " winMode=" + mTaskInfo1.getWindowingMode());
+ }
+ if (mTaskInfo2 != null) {
+ pw.println(innerPrefix + "2 taskId=" + mTaskInfo2.taskId
+ + " winMode=" + mTaskInfo2.getWindowingMode());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return TAG + "#" + getRootTaskId();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java
new file mode 100644
index 000000000000..f8703f7ec0bc
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java
@@ -0,0 +1,224 @@
+/*
+ * 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.apppairs;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+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_SPLIT_TOUCH;
+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.TYPE_DOCK_DIVIDER;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Binder;
+import android.os.IBinder;
+import android.view.Display;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import com.android.wm.shell.R;
+
+/**
+ * Records and handles layout of a pair of apps.
+ */
+// TODO(172704238): add tests
+final class AppPairLayout {
+ private static final String DIVIDER_WINDOW_TITLE = "AppPairDivider";
+ private final Context mContext;
+ private final AppPairWindowManager mAppPairWindowManager;
+ private final SurfaceControlViewHost mViewHost;
+
+ private final int mDividerWindowWidth;
+ private final int mDividerWindowInsets;
+
+ private boolean mIsLandscape;
+ private Rect mRootBounds;
+ private DIVIDE_POLICY mDividePolicy;
+
+ private DividerView mDividerView;
+ private SurfaceControl mDividerLeash;
+
+ AppPairLayout(
+ Context context,
+ Display display,
+ Configuration configuration,
+ SurfaceControl rootLeash) {
+ mContext = context.createConfigurationContext(configuration);
+ mIsLandscape = isLandscape(configuration);
+ mRootBounds = configuration.windowConfiguration.getBounds();
+ mDividerWindowWidth = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ mDividerWindowInsets = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_insets);
+
+ mAppPairWindowManager = new AppPairWindowManager(configuration, rootLeash);
+ mViewHost = new SurfaceControlViewHost(mContext, display, mAppPairWindowManager);
+ mDividePolicy = DIVIDE_POLICY.MIDDLE;
+ mDividePolicy.update(mIsLandscape, mRootBounds, mDividerWindowWidth, mDividerWindowInsets);
+ }
+
+ boolean updateConfiguration(Configuration configuration) {
+ mAppPairWindowManager.setConfiguration(configuration);
+ final Rect rootBounds = configuration.windowConfiguration.getBounds();
+ final boolean isLandscape = isLandscape(configuration);
+ if (mIsLandscape == isLandscape && isIdenticalBounds(mRootBounds, rootBounds)) {
+ return false;
+ }
+
+ mIsLandscape = isLandscape;
+ mRootBounds = rootBounds;
+ mDividePolicy.update(mIsLandscape, mRootBounds, mDividerWindowWidth, mDividerWindowInsets);
+ mViewHost.relayout(
+ mDividePolicy.mDividerBounds.width(),
+ mDividePolicy.mDividerBounds.height());
+ // TODO(172704238): handle divider bar rotation.
+ return true;
+ }
+
+ Rect getBounds1() {
+ return mDividePolicy.mBounds1;
+ }
+
+ Rect getBounds2() {
+ return mDividePolicy.mBounds2;
+ }
+
+ Rect getDividerBounds() {
+ return mDividePolicy.mDividerBounds;
+ }
+
+ SurfaceControl getDividerLeash() {
+ return mDividerLeash;
+ }
+
+ void release() {
+ if (mViewHost == null) return;
+ mViewHost.release();
+ }
+
+ void setDividerVisibility(boolean visible) {
+ if (mDividerView == null) {
+ initDivider();
+ }
+ if (visible) {
+ mDividerView.show();
+ } else {
+ mDividerView.hide();
+ }
+ }
+
+ private void initDivider() {
+ final DividerView dividerView = (DividerView) LayoutInflater.from(mContext)
+ .inflate(R.layout.split_divider, null);
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ mDividePolicy.mDividerBounds.width(),
+ mDividePolicy.mDividerBounds.height(),
+ TYPE_DOCK_DIVIDER,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH
+ | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY,
+ PixelFormat.TRANSLUCENT);
+ lp.token = new Binder();
+ lp.setTitle(DIVIDER_WINDOW_TITLE);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+
+ mViewHost.setView(dividerView, lp);
+ mDividerView = dividerView;
+ mDividerLeash = mAppPairWindowManager.getSurfaceControl(mViewHost.getWindowToken());
+ }
+
+ private static boolean isLandscape(Configuration configuration) {
+ return configuration.orientation == ORIENTATION_LANDSCAPE;
+ }
+
+ private static boolean isIdenticalBounds(Rect bounds1, Rect bounds2) {
+ return bounds1.left == bounds2.left && bounds1.top == bounds2.top
+ && bounds1.right == bounds2.right && bounds1.bottom == bounds2.bottom;
+ }
+
+ /**
+ * Indicates the policy of placing divider bar and corresponding split-screens.
+ */
+ // TODO(172704238): add more divide policy and provide snap to resize feature for divider bar.
+ enum DIVIDE_POLICY {
+ MIDDLE;
+
+ void update(boolean isLandscape, Rect rootBounds, int dividerWindowWidth,
+ int dividerWindowInsets) {
+ final int dividerOffset = dividerWindowWidth / 2;
+ final int boundsOffset = dividerOffset - dividerWindowInsets;
+
+ mDividerBounds = new Rect(rootBounds);
+ mBounds1 = new Rect(rootBounds);
+ mBounds2 = new Rect(rootBounds);
+
+ switch (this) {
+ case MIDDLE:
+ default:
+ if (isLandscape) {
+ mDividerBounds.left = rootBounds.width() / 2 - dividerOffset;
+ mDividerBounds.right = rootBounds.width() / 2 + dividerOffset;
+ mBounds1.left = rootBounds.width() / 2 + boundsOffset;
+ mBounds2.right = rootBounds.width() / 2 - boundsOffset;
+ } else {
+ mDividerBounds.top = rootBounds.height() / 2 - dividerOffset;
+ mDividerBounds.bottom = rootBounds.height() / 2 + dividerOffset;
+ mBounds1.bottom = rootBounds.height() / 2 - boundsOffset;
+ mBounds2.top = rootBounds.height() / 2 + boundsOffset;
+ }
+ }
+ }
+
+ Rect mDividerBounds;
+ Rect mBounds1;
+ Rect mBounds2;
+ }
+
+ /**
+ * WindowManger for app pair. Holds view hierarchy for the root task.
+ */
+ private static final class AppPairWindowManager extends WindowlessWindowManager {
+ AppPairWindowManager(Configuration config, SurfaceControl rootSurface) {
+ super(config, rootSurface, null /* hostInputToken */);
+ }
+
+ @Override
+ public void setTouchRegion(IBinder window, Region region) {
+ super.setTouchRegion(window, region);
+ }
+
+ @Override
+ public SurfaceControl getSurfaceControl(IWindow window) {
+ return super.getSurfaceControl(window);
+ }
+
+ @Override
+ public void setConfiguration(Configuration configuration) {
+ super.setConfiguration(configuration);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
new file mode 100644
index 000000000000..af06764145e3
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
@@ -0,0 +1,41 @@
+/*
+ * 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.apppairs;
+
+import android.app.ActivityManager;
+
+import androidx.annotation.NonNull;
+
+import java.io.PrintWriter;
+
+/**
+ * Interface to engage app pairs feature.
+ */
+public interface AppPairs {
+ /** Pairs indicated tasks. */
+ boolean pair(int task1, int task2);
+ /** Pairs indicated tasks. */
+ boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2);
+ /** Unpairs any app-pair containing this task id. */
+ void unpair(int taskId);
+ /** Dumps current status of app pairs. */
+ void dump(@NonNull PrintWriter pw, String prefix);
+ /** Called when the shell organizer has been registered. */
+ void onOrganizerRegistered();
+ /** Called when the visibility of the keyguard changes. */
+ void onKeyguardVisibilityChanged(boolean showing);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
new file mode 100644
index 000000000000..925a4f36d5e6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
@@ -0,0 +1,185 @@
+/*
+ * 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.apppairs;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+
+import android.app.ActivityManager;
+import android.util.SparseArray;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TaskStackListenerCallback;
+import com.android.wm.shell.common.TaskStackListenerImpl;
+
+import java.io.PrintWriter;
+
+/**
+ * Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}.
+ */
+public class AppPairsController implements AppPairs, TaskStackListenerCallback {
+ private static final String TAG = AppPairsController.class.getSimpleName();
+
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final SyncTransactionQueue mSyncQueue;
+
+ private AppPairsPool mPairsPool;
+ // Active app-pairs mapped by root task id key.
+ private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
+ private final DisplayController mDisplayController;
+ private int mForegroundTaskId = INVALID_TASK_ID;
+
+ public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
+ DisplayController displayController, TaskStackListenerImpl taskStackListener) {
+ mTaskOrganizer = organizer;
+ mSyncQueue = syncQueue;
+ mDisplayController = displayController;
+ taskStackListener.addListener(this);
+ }
+
+ @Override
+ public void onOrganizerRegistered() {
+ if (mPairsPool == null) {
+ setPairsPool(new AppPairsPool(this));
+ }
+ }
+
+ @VisibleForTesting
+ void setPairsPool(AppPairsPool pool) {
+ mPairsPool = pool;
+ }
+
+ @Override
+ public void onTaskMovedToFront(int taskId) {
+ mForegroundTaskId = INVALID_TASK_ID;
+ for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
+ final AppPair candidate = mActiveAppPairs.valueAt(i);
+ final boolean containForegroundTask = candidate.contains(taskId);
+ candidate.setVisible(containForegroundTask);
+ if (containForegroundTask) {
+ mForegroundTaskId = candidate.getRootTaskId();
+ }
+ }
+ }
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (mForegroundTaskId == INVALID_TASK_ID) {
+ return;
+ }
+ mActiveAppPairs.get(mForegroundTaskId).setVisible(!showing);
+ }
+
+ @Override
+ public boolean pair(int taskId1, int taskId2) {
+ final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1);
+ final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2);
+ if (task1 == null || task2 == null) {
+ return false;
+ }
+ return pair(task1, task2);
+ }
+
+ @Override
+ public boolean pair(ActivityManager.RunningTaskInfo task1,
+ ActivityManager.RunningTaskInfo task2) {
+ return pairInner(task1, task2) != null;
+ }
+
+ @VisibleForTesting
+ AppPair pairInner(
+ @NonNull ActivityManager.RunningTaskInfo task1,
+ @NonNull ActivityManager.RunningTaskInfo task2) {
+ final AppPair pair = mPairsPool.acquire();
+ if (!pair.pair(task1, task2)) {
+ mPairsPool.release(pair);
+ return null;
+ }
+
+ mActiveAppPairs.put(pair.getRootTaskId(), pair);
+ return pair;
+ }
+
+ @Override
+ public void unpair(int taskId) {
+ unpair(taskId, true /* releaseToPool */);
+ }
+
+ void unpair(int taskId, boolean releaseToPool) {
+ AppPair pair = mActiveAppPairs.get(taskId);
+ if (pair == null) {
+ for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
+ final AppPair candidate = mActiveAppPairs.valueAt(i);
+ if (candidate.contains(taskId)) {
+ pair = candidate;
+ break;
+ }
+ }
+ }
+ if (pair == null) {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "taskId %d isn't isn't in an app-pair.", taskId);
+ return;
+ }
+
+ ProtoLog.v(WM_SHELL_TASK_ORG, "unpair taskId=%d pair=%s", taskId, pair);
+ mActiveAppPairs.remove(pair.getRootTaskId());
+ pair.unpair();
+ if (releaseToPool) {
+ mPairsPool.release(pair);
+ }
+ }
+
+ ShellTaskOrganizer getTaskOrganizer() {
+ return mTaskOrganizer;
+ }
+
+ SyncTransactionQueue getSyncTransactionQueue() {
+ return mSyncQueue;
+ }
+
+ DisplayController getDisplayController() {
+ return mDisplayController;
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ final String childPrefix = innerPrefix + " ";
+ pw.println(prefix + this);
+
+ for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
+ mActiveAppPairs.valueAt(i).dump(pw, childPrefix);
+ }
+
+ if (mPairsPool != null) {
+ mPairsPool.dump(pw, prefix);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return TAG + "#" + mActiveAppPairs.size();
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java
new file mode 100644
index 000000000000..5c6037ea0702
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java
@@ -0,0 +1,93 @@
+/*
+ * 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.apppairs;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Class that manager pool of {@link AppPair} objects. Helps reduce the need to call system_server
+ * to create a root task for the app-pair when needed since we always have one ready to go.
+ */
+class AppPairsPool {
+ private static final String TAG = AppPairsPool.class.getSimpleName();
+
+ @VisibleForTesting
+ final AppPairsController mController;
+ // The pool
+ private final ArrayList<AppPair> mPool = new ArrayList();
+
+ AppPairsPool(AppPairsController controller) {
+ mController = controller;
+ incrementPool();
+ }
+
+ AppPair acquire() {
+ final AppPair entry = mPool.remove(mPool.size() - 1);
+ ProtoLog.v(WM_SHELL_TASK_ORG, "acquire entry.taskId=%s listener=%s size=%d",
+ entry.getRootTaskId(), entry, mPool.size());
+ if (mPool.size() == 0) {
+ incrementPool();
+ }
+ return entry;
+ }
+
+ void release(AppPair entry) {
+ mPool.add(entry);
+ ProtoLog.v(WM_SHELL_TASK_ORG, "release entry.taskId=%s listener=%s size=%d",
+ entry.getRootTaskId(), entry, mPool.size());
+ }
+
+ @VisibleForTesting
+ void incrementPool() {
+ ProtoLog.v(WM_SHELL_TASK_ORG, "incrementPool size=%d", mPool.size());
+ final AppPair entry = new AppPair(mController);
+ // TODO: multi-display...
+ mController.getTaskOrganizer().createRootTask(
+ DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN, entry);
+ mPool.add(entry);
+ }
+
+ @VisibleForTesting
+ int poolSize() {
+ return mPool.size();
+ }
+
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ final String childPrefix = innerPrefix + " ";
+ pw.println(prefix + this);
+ for (int i = mPool.size() - 1; i >= 0; --i) {
+ mPool.get(i).dump(pw, childPrefix);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return TAG + "#" + mPool.size();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java
new file mode 100644
index 000000000000..41b5e47e7fa9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/DividerView.java
@@ -0,0 +1,56 @@
+/*
+ * 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.apppairs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Stack divider for app pair.
+ */
+public class DividerView extends FrameLayout {
+ public DividerView(@NonNull Context context) {
+ super(context);
+ }
+
+ public DividerView(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public DividerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ void show() {
+ post(() -> setVisibility(View.VISIBLE));
+ }
+
+ void hide() {
+ post(() -> setVisibility(View.INVISIBLE));
+ }
+}
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 eb3a3a276727..aa7355b61eda 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
@@ -24,6 +24,10 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_BOTTOM;
+import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_LEFT;
+import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_NONE;
+import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_RIGHT;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -38,7 +42,9 @@ import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
+import android.graphics.PointF;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -81,6 +87,19 @@ public class BubbleController implements Bubbles {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
+ // TODO(b/173386799) keep in sync with Launcher3 and also don't do a broadcast
+ public static final String TASKBAR_CHANGED_BROADCAST = "taskbarChanged";
+ public static final String EXTRA_TASKBAR_CREATED = "taskbarCreated";
+ public static final String EXTRA_BUBBLE_OVERFLOW_OPENED = "bubbleOverflowOpened";
+ public static final String EXTRA_TASKBAR_VISIBLE = "taskbarVisible";
+ public static final String EXTRA_TASKBAR_POSITION = "taskbarPosition";
+ public static final String EXTRA_TASKBAR_ICON_SIZE = "taskbarIconSize";
+ public static final String EXTRA_TASKBAR_BUBBLE_XY = "taskbarBubbleXY";
+ public static final String EXTRA_TASKBAR_SIZE = "taskbarSize";
+ public static final String LEFT_POSITION = "Left";
+ public static final String RIGHT_POSITION = "Right";
+ public static final String BOTTOM_POSITION = "Bottom";
+
private final Context mContext;
private BubbleExpandListener mExpandListener;
@Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
@@ -295,6 +314,60 @@ public class BubbleController implements Bubbles {
}
}
+ @Override
+ public void openBubbleOverflow() {
+ ensureStackViewCreated();
+ mBubbleData.setShowingOverflow(true);
+ mBubbleData.setSelectedBubble(mBubbleData.getOverflow());
+ mBubbleData.setExpanded(true);
+ }
+
+ /** Called when any taskbar state changes (e.g. visibility, position, sizes). */
+ @Override
+ public void onTaskbarChanged(Bundle b) {
+ if (b == null) {
+ return;
+ }
+ boolean isVisible = b.getBoolean(EXTRA_TASKBAR_VISIBLE, false /* default */);
+ String position = b.getString(EXTRA_TASKBAR_POSITION, RIGHT_POSITION /* default */);
+ @BubblePositioner.TaskbarPosition int taskbarPosition = TASKBAR_POSITION_NONE;
+ switch (position) {
+ case LEFT_POSITION:
+ taskbarPosition = TASKBAR_POSITION_LEFT;
+ break;
+ case RIGHT_POSITION:
+ taskbarPosition = TASKBAR_POSITION_RIGHT;
+ break;
+ case BOTTOM_POSITION:
+ taskbarPosition = TASKBAR_POSITION_BOTTOM;
+ break;
+ }
+ int[] itemPosition = b.getIntArray(EXTRA_TASKBAR_BUBBLE_XY);
+ int iconSize = b.getInt(EXTRA_TASKBAR_ICON_SIZE);
+ int taskbarSize = b.getInt(EXTRA_TASKBAR_SIZE);
+ Log.w(TAG, "onTaskbarChanged:"
+ + " isVisible: " + isVisible
+ + " position: " + position
+ + " itemPosition: " + itemPosition[0] + "," + itemPosition[1]
+ + " iconSize: " + iconSize);
+ PointF point = new PointF(itemPosition[0], itemPosition[1]);
+ mBubblePositioner.setPinnedLocation(isVisible ? point : null);
+ mBubblePositioner.updateForTaskbar(iconSize, taskbarPosition, isVisible, taskbarSize);
+ if (mStackView != null) {
+ if (isVisible && b.getBoolean(EXTRA_TASKBAR_CREATED, false /* default */)) {
+ // If taskbar was created, add and remove the window so that bubbles display on top
+ removeFromWindowManagerMaybe();
+ addToWindowManagerMaybe();
+ }
+ mStackView.updateStackPosition();
+ mBubbleIconFactory = new BubbleIconFactory(mContext);
+ mStackView.onDisplaySizeChanged();
+ }
+ if (b.getBoolean(EXTRA_BUBBLE_OVERFLOW_OPENED, false)) {
+ openBubbleOverflow();
+ }
+ }
+
/**
* 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/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 646e75a67867..318a0bd42940 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
@@ -29,6 +29,7 @@ import static com.android.wm.shell.bubbles.BubbleOverflowActivity.EXTRA_BUBBLE_C
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -41,6 +42,7 @@ import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.os.Bundle;
+import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceControl;
@@ -394,7 +396,7 @@ public class BubbleExpandedView extends LinearLayout {
/** Return a GraphicBuffer with the contents of the task view surface. */
@Nullable
SurfaceControl.ScreenshotHardwareBuffer snapshotActivitySurface() {
- if (mTaskView == null) {
+ if (mTaskView == null || mTaskView.getSurfaceControl() == null) {
return null;
}
return SurfaceControl.captureLayers(
@@ -647,6 +649,13 @@ public class BubbleExpandedView extends LinearLayout {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "cleanUpExpandedState: bubble=" + getBubbleKey() + " task=" + mTaskId);
}
+ if (getTaskId() != INVALID_TASK_ID) {
+ try {
+ ActivityTaskManager.getService().removeTask(getTaskId());
+ } catch (RemoteException e) {
+ Log.w(TAG, e.getMessage());
+ }
+ }
if (mTaskView != null) {
mTaskView.release();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 46e8e11610ac..1562e4bf6fcc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -48,8 +48,10 @@ public class BubblePositioner {
: BubbleDebugConfig.TAG_BUBBLES;
@Retention(SOURCE)
- @IntDef({TASKBAR_POSITION_RIGHT, TASKBAR_POSITION_LEFT, TASKBAR_POSITION_BOTTOM})
+ @IntDef({TASKBAR_POSITION_NONE, TASKBAR_POSITION_RIGHT, TASKBAR_POSITION_LEFT,
+ TASKBAR_POSITION_BOTTOM})
@interface TaskbarPosition {}
+ public static final int TASKBAR_POSITION_NONE = -1;
public static final int TASKBAR_POSITION_RIGHT = 0;
public static final int TASKBAR_POSITION_LEFT = 1;
public static final int TASKBAR_POSITION_BOTTOM = 2;
@@ -73,8 +75,9 @@ public class BubblePositioner {
private PointF mRestingStackPosition;
private boolean mShowingInTaskbar;
- private @TaskbarPosition int mTaskbarPosition;
+ private @TaskbarPosition int mTaskbarPosition = TASKBAR_POSITION_NONE;
private int mTaskbarIconSize;
+ private int mTaskbarSize;
public BubblePositioner(Context context, WindowManager windowManager) {
mContext = context;
@@ -107,6 +110,21 @@ public class BubblePositioner {
updateInternal(orientation, insets, windowMetrics.getBounds());
}
+ /**
+ * Updates position information to account for taskbar state.
+ *
+ * @param taskbarPosition which position the taskbar is displayed in.
+ * @param showingInTaskbar whether the taskbar is being shown.
+ */
+ public void updateForTaskbar(int iconSize,
+ @TaskbarPosition int taskbarPosition, boolean showingInTaskbar, int taskbarSize) {
+ mShowingInTaskbar = showingInTaskbar;
+ mTaskbarIconSize = iconSize;
+ mTaskbarPosition = taskbarPosition;
+ mTaskbarSize = taskbarSize;
+ update(mOrientation);
+ }
+
@VisibleForTesting
public void updateInternal(int orientation, Insets insets, Rect bounds) {
mOrientation = orientation;
@@ -121,21 +139,9 @@ public class BubblePositioner {
Resources res = mContext.getResources();
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size);
- adjustForTaskbar();
- }
-
- /**
- * Updates position information to account for taskbar state.
- *
- * @param taskbarPosition which position the taskbar is displayed in.
- * @param showingInTaskbar whether the taskbar is being shown.
- */
- public void updateForTaskbar(int iconSize,
- @TaskbarPosition int taskbarPosition, boolean showingInTaskbar) {
- mShowingInTaskbar = showingInTaskbar;
- mTaskbarIconSize = iconSize;
- mTaskbarPosition = taskbarPosition;
- update(mOrientation);
+ if (mShowingInTaskbar) {
+ adjustForTaskbar();
+ }
}
/**
@@ -263,6 +269,17 @@ public class BubblePositioner {
}
/**
+ * @return the taskbar position if set.
+ */
+ public int getTaskbarPosition() {
+ return mTaskbarPosition;
+ }
+
+ public int getTaskbarSize() {
+ return mTaskbarSize;
+ }
+
+ /**
* In some situations bubbles will be pinned to a specific onscreen location. This sets the
* location to anchor the stack to.
*/
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 cbe98458df86..1e5e43a99b09 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
@@ -32,6 +32,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Insets;
@@ -194,6 +195,7 @@ public class BubbleStackView extends FrameLayout
private StackAnimationController mStackAnimationController;
private ExpandedAnimationController mExpandedAnimationController;
+ private View mTaskbarScrim;
private FrameLayout mExpandedViewContainer;
/** Matrix used to scale the expanded view container with a given pivot point. */
@@ -631,7 +633,9 @@ public class BubbleStackView extends FrameLayout
public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
float viewInitialY, float dx, float dy, float velX, float velY) {
// If we're expanding or collapsing, ignore all touch events.
- if (mIsExpansionAnimating) {
+ if (mIsExpansionAnimating
+ // Also ignore events if we shouldn't be draggable.
+ || (mPositioner.showingInTaskbar() && !mIsExpanded)) {
return;
}
@@ -741,14 +745,13 @@ public class BubbleStackView extends FrameLayout
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding);
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
-
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
- mPositioner = mBubbleController.getPositioner();
-
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
+ mPositioner = mBubbleController.getPositioner();
+
final TypedArray ta = mContext.obtainStyledAttributes(
new int[] {android.R.attr.dialogCornerRadius});
mCornerRadius = ta.getDimensionPixelSize(0, 0);
@@ -840,6 +843,12 @@ public class BubbleStackView extends FrameLayout
mBubbleData.setExpanded(true);
});
+ mTaskbarScrim = new View(getContext());
+ mTaskbarScrim.setBackgroundColor(Color.BLACK);
+ addView(mTaskbarScrim);
+ mTaskbarScrim.setAlpha(0f);
+ mTaskbarScrim.setVisibility(GONE);
+
setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
mBubbleController.onImeVisibilityChanged(
insets.getInsets(WindowInsets.Type.ime()).bottom > 0);
@@ -1631,6 +1640,7 @@ public class BubbleStackView extends FrameLayout
/** Set the stack position to whatever the positioner says. */
void updateStackPosition() {
mStackAnimationController.setStackPosition(mPositioner.getRestingPosition());
+ mDismissView.hide();
}
private void beforeExpandedViewAnimation() {
@@ -1664,6 +1674,17 @@ public class BubbleStackView extends FrameLayout
}
} /* after */);
+ if (mPositioner.showingInTaskbar()
+ // Don't need the scrim when the bar is at the bottom
+ && mPositioner.getTaskbarPosition() != BubblePositioner.TASKBAR_POSITION_BOTTOM) {
+ mTaskbarScrim.getLayoutParams().width = mPositioner.getTaskbarSize();
+ mTaskbarScrim.setTranslationX(mStackOnLeftOrWillBe
+ ? 0f
+ : mPositioner.getAvailableRect().right - mPositioner.getTaskbarSize());
+ mTaskbarScrim.setVisibility(VISIBLE);
+ mTaskbarScrim.animate().alpha(1f).start();
+ }
+
mExpandedViewContainer.setTranslationX(0f);
mExpandedViewContainer.setTranslationY(getExpandedViewY());
mExpandedViewContainer.setAlpha(1f);
@@ -1781,6 +1802,10 @@ public class BubbleStackView extends FrameLayout
/* collapseTo */,
() -> mBubbleContainer.setActiveController(mStackAnimationController)), startDelay);
+ if (mTaskbarScrim.getVisibility() == VISIBLE) {
+ mTaskbarScrim.animate().alpha(0f).start();
+ }
+
// We want to visually collapse into this bubble during the animation.
final View expandingFromBubble = mExpandedBubble.getIconView();
@@ -1856,6 +1881,10 @@ public class BubbleStackView extends FrameLayout
if (previouslySelected != null) {
previouslySelected.setContentVisibility(false);
}
+
+ if (mPositioner.showingInTaskbar()) {
+ mTaskbarScrim.setVisibility(GONE);
+ }
})
.start();
}
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 79c42d83d7ea..92d15c5feaca 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
@@ -22,6 +22,7 @@ import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.content.res.Configuration;
+import android.os.Bundle;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
import android.view.View;
@@ -104,6 +105,12 @@ public interface Bubbles {
*/
void expandStackAndSelectBubble(BubbleEntry entry);
+ /** Called for any taskbar changes. */
+ void onTaskbarChanged(Bundle b);
+
+ /** Open the overflow view. */
+ void openBubbleOverflow();
+
/**
* We intercept notification entries (including group summaries) dismissed by the user when
* there is an active bubble associated with it. We do this so that developers can still
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 24a8809d3f2b..96398be42fb2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -769,7 +769,6 @@ public class StackAnimationController extends
if (getBubbleCount() > 0) {
animationForChildAtIndex(0).translationX(mStackPosition.x).start();
} else {
- // TODO: still needed with positioner?
// When all children are removed ensure stack position is sane
mPositioner.setRestingPosition(mPositioner.getRestingPosition());
@@ -939,45 +938,6 @@ public class StackAnimationController extends
setStackPosition(position.getAbsolutePositionInRegion(getAllowableStackPositionRegion()));
}
- public BubbleStackView.RelativeStackPosition getRelativeStackPosition() {
- return new BubbleStackView.RelativeStackPosition(
- mStackPosition, getAllowableStackPositionRegion());
- }
-
- /**
- * Sets the starting position for the stack, where it will be located when the first bubble is
- * added.
- */
- public void setStackStartPosition(BubbleStackView.RelativeStackPosition position) {
- mStackStartPosition = position;
- }
-
- /**
- * Returns the starting stack position. If {@link #setStackStartPosition} was called, this will
- * return that position - otherwise, a reasonable default will be returned.
- */
- @Nullable public PointF getStartPosition() {
- if (mLayout == null) {
- return null;
- }
-
- if (mStackStartPosition == null) {
- // Start on the left if we're in LTR, right otherwise.
- final boolean startOnLeft =
- mLayout.getResources().getConfiguration().getLayoutDirection()
- != View.LAYOUT_DIRECTION_RTL;
-
- final float startingVerticalOffset = mLayout.getResources().getDimensionPixelOffset(
- R.dimen.bubble_stack_starting_offset_y);
-
- mStackStartPosition = new BubbleStackView.RelativeStackPosition(
- startOnLeft,
- startingVerticalOffset / getAllowableStackPositionRegion().height());
- }
-
- return mStackStartPosition.getAbsolutePositionInRegion(getAllowableStackPositionRegion());
- }
-
private boolean isStackPositionSet() {
return mStackMovedToStartPosition;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxConfigController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxConfigController.java
new file mode 100644
index 000000000000..0a549c6aa7d9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxConfigController.java
@@ -0,0 +1,108 @@
+/*
+ * 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.letterbox;
+
+import android.content.Context;
+import android.view.Gravity;
+
+import com.android.wm.shell.R;
+
+/**
+ * Controls access to and overrides of resource config values used by {@link
+ * LetterboxTaskOrganizer}.
+ */
+public final class LetterboxConfigController {
+
+ private final Context mContext;
+
+ /** {@link Gravity} of letterboxed apps in portrait screen orientation. */
+ private int mLetterboxPortraitGravity;
+
+ /** {@link Gravity} of letterboxed apps in landscape screen orientation. */
+ private int mLetterboxLandscapeGravity;
+
+ public LetterboxConfigController(Context context) {
+ mContext = context;
+ mLetterboxPortraitGravity =
+ mContext.getResources().getInteger(R.integer.config_letterboxPortraitGravity);
+ mLetterboxLandscapeGravity =
+ mContext.getResources().getInteger(R.integer.config_letterboxLandscapeGravity);
+ }
+
+ /**
+ * Overrides {@link Gravity} of letterboxed apps in portrait screen orientation.
+ *
+ * @throws IllegalArgumentException if gravity isn't equal to {@link Gravity#TOP}, {@link
+ * Gravity#CENTER} or {@link Gravity#BOTTOM}.
+ */
+ public void setPortraitGravity(int gravity) {
+ if (gravity != Gravity.TOP && gravity != Gravity.CENTER && gravity != Gravity.BOTTOM) {
+ throw new IllegalArgumentException(
+ "Expected Gravity#TOP, Gravity#CENTER or Gravity#BOTTOM but got"
+ + gravity);
+ }
+ mLetterboxPortraitGravity = gravity;
+ }
+
+ /**
+ * Resets {@link Gravity} of letterboxed apps in portrait screen orientation to {@link
+ * R.integer.config_letterboxPortraitGravity}.
+ */
+ public void resetPortraitGravity() {
+ mLetterboxPortraitGravity =
+ mContext.getResources().getInteger(R.integer.config_letterboxPortraitGravity);
+ }
+
+ /**
+ * Gets {@link Gravity} of letterboxed apps in portrait screen orientation.
+ */
+ public int getPortraitGravity() {
+ return mLetterboxPortraitGravity;
+ }
+
+ /**
+ * Overrides {@link Gravity} of letterboxed apps in landscape screen orientation.
+ *
+ * @throws IllegalArgumentException if gravity isn't equal to {@link Gravity#RIGHT}, {@link
+ * Gravity#CENTER} or {@link Gravity#LEFT}.
+ */
+ public void setLandscapeGravity(int gravity) {
+ if (gravity != Gravity.LEFT && gravity != Gravity.CENTER && gravity != Gravity.RIGHT) {
+ throw new IllegalArgumentException(
+ "Expected Gravity#LEFT, Gravity#CENTER or Gravity#RIGHT but got"
+ + gravity);
+ }
+ mLetterboxLandscapeGravity = gravity;
+ }
+
+ /**
+ * Resets {@link Gravity} of letterboxed apps in landscape screen orientation to {@link
+ * R.integer.config_letterboxLandscapeGravity}.
+ */
+ public void resetLandscapeGravity() {
+ mLetterboxLandscapeGravity =
+ mContext.getResources().getInteger(R.integer.config_letterboxLandscapeGravity);
+ }
+
+ /**
+ * Gets {@link Gravity} of letterboxed apps in landscape screen orientation.
+ */
+ public int getLandscapeGravity() {
+ return mLetterboxLandscapeGravity;
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java
new file mode 100644
index 000000000000..061d3f86b669
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java
@@ -0,0 +1,226 @@
+/*
+ * 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.letterbox;
+
+import android.app.ActivityManager;
+import android.graphics.Insets;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.SurfaceControl;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.Transitions;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+/**
+ * Organizes a task in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN} when
+ * it's presented in the letterbox mode either because orientations of a top activity and a device
+ * don't match or because a top activity is in a size compat mode.
+ */
+public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener {
+ private static final String TAG = "LetterboxTaskListener";
+
+ private final SyncTransactionQueue mSyncQueue;
+ private final LetterboxConfigController mLetterboxConfigController;
+ private final WindowManager mWindowManager;
+ private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>();
+
+ public LetterboxTaskListener(
+ SyncTransactionQueue syncQueue,
+ LetterboxConfigController letterboxConfigController,
+ WindowManager windowManager) {
+ mSyncQueue = syncQueue;
+ mLetterboxConfigController = letterboxConfigController;
+ mWindowManager = windowManager;
+ }
+
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ if (mLeashByTaskId.get(taskInfo.taskId) != null) {
+ throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId);
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Appeared: #%d",
+ taskInfo.taskId);
+ mLeashByTaskId.put(taskInfo.taskId, leash);
+ Point positionInParent = new Point();
+ Rect crop = new Rect();
+ resolveTaskPositionAndCrop(taskInfo, positionInParent, crop);
+ mSyncQueue.runInSync(t -> {
+ setPositionAndWindowCrop(t, leash, positionInParent, crop);
+ if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
+ t.setAlpha(leash, 1f);
+ t.setMatrix(leash, 1, 0, 0, 1);
+ t.show(leash);
+ }
+ });
+ }
+
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mLeashByTaskId.get(taskInfo.taskId) == null) {
+ Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
+ return;
+ }
+ mLeashByTaskId.remove(taskInfo.taskId);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Vanished: #%d",
+ taskInfo.taskId);
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Changed: #%d",
+ taskInfo.taskId);
+ final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId);
+ Point positionInParent = new Point();
+ Rect crop = new Rect();
+ resolveTaskPositionAndCrop(taskInfo, positionInParent, crop);
+ mSyncQueue.runInSync(t -> setPositionAndWindowCrop(t, leash, positionInParent, crop));
+ }
+
+ private static void setPositionAndWindowCrop(
+ SurfaceControl.Transaction transaction,
+ SurfaceControl leash,
+ final Point positionInParent,
+ final Rect crop) {
+ transaction.setPosition(leash, positionInParent.x, positionInParent.y);
+ transaction.setWindowCrop(leash, crop);
+ }
+
+ private void resolveTaskPositionAndCrop(
+ ActivityManager.RunningTaskInfo taskInfo,
+ Point positionInParent,
+ Rect crop) {
+ // In screen coordinates
+ Rect parentBounds = new Rect(taskInfo.parentBounds);
+ // Intersect parent and max bounds. This is required for situations when parent bounds
+ // go beyond display bounds, for example, in One-handed mode.
+ final Rect maxBounds = taskInfo.getConfiguration().windowConfiguration.getMaxBounds();
+ if (!parentBounds.intersect(maxBounds)) {
+ Slog.w(TAG, "Task parent and max bounds don't intersect: #" + taskInfo.taskId);
+ }
+
+ // In screen coordinates
+ final Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
+ final Rect activityBounds = taskInfo.letterboxActivityBounds;
+
+ Insets insets = getInsets();
+
+ Rect taskBoundsWithInsets = new Rect(taskBounds);
+ applyInsets(taskBoundsWithInsets, insets, taskInfo.parentBounds);
+
+ Rect activityBoundsWithInsets = new Rect(activityBounds);
+ applyInsets(activityBoundsWithInsets, insets, taskInfo.parentBounds);
+
+ Rect parentBoundsWithInsets = new Rect(parentBounds);
+ applyInsets(parentBoundsWithInsets, insets, parentBounds);
+
+ // Crop need to be in the task coordinates.
+ crop.set(activityBoundsWithInsets);
+ crop.offset(-taskBounds.left, -taskBounds.top);
+
+ // Account for insets since coordinates calculations below are done with them.
+ positionInParent.x = parentBoundsWithInsets.left - parentBounds.left
+ - (taskBoundsWithInsets.left - taskBounds.left);
+ positionInParent.y = parentBoundsWithInsets.top - parentBounds.top
+ - (taskBoundsWithInsets.top - taskBounds.top);
+
+ // Calculating a position of task bounds (without insets) in parent coordinates (without
+ // insets) to align activity bounds (without insets) as requested in config. Activity
+ // accounts for insets that overlap with its bounds (this overlap can be partial) so
+ // ignoring overlap with insets when computing the position. Also, cropping unwanted insets
+ // while keeping the top one if the activity is aligned at the top of the window to show
+ // status bar decor view.
+ if (parentBounds.height() >= parentBounds.width()) {
+ final int gravity = mLetterboxConfigController.getPortraitGravity();
+ // Center activity horizontally.
+ positionInParent.x +=
+ (parentBoundsWithInsets.width() - activityBoundsWithInsets.width()) / 2
+ + taskBoundsWithInsets.left - activityBoundsWithInsets.left;
+ switch (gravity) {
+ case Gravity.TOP:
+ positionInParent.y += taskBoundsWithInsets.top - activityBoundsWithInsets.top;
+ // Showing status bar decor view.
+ crop.top -= activityBoundsWithInsets.top - activityBounds.top;
+ break;
+ case Gravity.CENTER:
+ positionInParent.y +=
+ taskBoundsWithInsets.top - activityBoundsWithInsets.top
+ + (parentBoundsWithInsets.height()
+ - activityBoundsWithInsets.height()) / 2;
+ break;
+ case Gravity.BOTTOM:
+ positionInParent.y +=
+ parentBoundsWithInsets.height() - activityBoundsWithInsets.bottom
+ + taskBoundsWithInsets.top;
+ break;
+ default:
+ throw new AssertionError(
+ "Unexpected portrait gravity " + gravity
+ + " for task: #" + taskInfo.taskId);
+ }
+ } else {
+ final int gravity = mLetterboxConfigController.getLandscapeGravity();
+ // Align activity to the top.
+ positionInParent.y += taskBoundsWithInsets.top - activityBoundsWithInsets.top;
+ // Showing status bar decor view.
+ crop.top -= activityBoundsWithInsets.top - activityBounds.top;
+ switch (gravity) {
+ case Gravity.LEFT:
+ positionInParent.x += taskBoundsWithInsets.left - activityBoundsWithInsets.left;
+ break;
+ case Gravity.CENTER:
+ positionInParent.x +=
+ (parentBoundsWithInsets.width() - activityBoundsWithInsets.width()) / 2
+ + taskBoundsWithInsets.left - activityBoundsWithInsets.left;
+ break;
+ case Gravity.RIGHT:
+ positionInParent.x +=
+ parentBoundsWithInsets.width()
+ - activityBoundsWithInsets.right + taskBoundsWithInsets.left;
+ break;
+ default:
+ throw new AssertionError(
+ "Unexpected landscape gravity " + gravity
+ + " for task: #" + taskInfo.taskId);
+ }
+ }
+ }
+
+ private Insets getInsets() {
+ return mWindowManager
+ .getMaximumWindowMetrics()
+ .getWindowInsets()
+ .getInsets(
+ WindowInsets.Type.navigationBars()
+ | WindowInsets.Type.statusBars()
+ | WindowInsets.Type.displayCutout());
+ }
+
+ private void applyInsets(Rect innerBounds, Insets insets, Rect outerBounds) {
+ Rect outerBoundsWithInsets = new Rect(outerBounds);
+ outerBoundsWithInsets.inset(insets);
+ innerBounds.intersect(outerBoundsWithInsets);
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index ca16cfc5141b..1bb5eda25058 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -16,42 +16,36 @@
package com.android.wm.shell.pip;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
import android.annotation.NonNull;
-import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManager.RootTaskInfo;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
-import android.os.RemoteException;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.util.Size;
import android.util.TypedValue;
import android.view.DisplayInfo;
import android.view.Gravity;
-import android.window.WindowContainerTransaction;
import java.io.PrintWriter;
/**
- * Handles bounds calculation for PIP on Phone and other form factors, it keeps tracking variant
- * state changes originated from Window Manager and is the source of truth for PiP window bounds.
+ * Calculates the default, normal, entry, inset and movement bounds of the PIP.
*/
-public class PipBoundsHandler {
+public class PipBoundsAlgorithm {
- private static final String TAG = PipBoundsHandler.class.getSimpleName();
+ private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
private final @NonNull PipBoundsState mPipBoundsState;
private final PipSnapAlgorithm mSnapAlgorithm;
+ private float mDefaultSizePercent;
+ private float mMinAspectRatioForMinSize;
+ private float mMaxAspectRatioForMinSize;
private float mDefaultAspectRatio;
private float mMinAspectRatio;
private float mMaxAspectRatio;
@@ -59,9 +53,9 @@ public class PipBoundsHandler {
private int mDefaultMinSize;
private Point mScreenEdgeInsets;
- public PipBoundsHandler(Context context, @NonNull PipBoundsState pipBoundsState) {
+ public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState) {
mPipBoundsState = pipBoundsState;
- mSnapAlgorithm = new PipSnapAlgorithm(context);
+ mSnapAlgorithm = new PipSnapAlgorithm();
reloadResources(context);
// Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
// resources as it would clobber mAspectRatio when entering PiP from fullscreen which
@@ -93,24 +87,11 @@ public class PipBoundsHandler {
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
mMaxAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
- }
-
- /**
- * Responds to IPinnedStackListener on movement bounds change.
- * Note that both inset and normal bounds will be calculated here rather than in the caller.
- */
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds) {
- getInsetBounds(insetBounds);
- final Rect defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION, null);
- normalBounds.set(defaultBounds);
- if (animatingBounds.isEmpty()) {
- animatingBounds.set(defaultBounds);
- }
- if (isValidPictureInPictureAspectRatio(mPipBoundsState.getAspectRatio())) {
- transformBoundsToAspectRatio(normalBounds, mPipBoundsState.getAspectRatio(),
- false /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
- }
+ mDefaultSizePercent = res.getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
+ mMaxAspectRatioForMinSize = res.getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
+ mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
}
/**
@@ -128,6 +109,19 @@ public class PipBoundsHandler {
reloadResources(context);
}
+ /** Returns the normal bounds (i.e. the default entry bounds). */
+ public Rect getNormalBounds() {
+ // The normal bounds are the default bounds adjusted to the current aspect ratio.
+ return transformBoundsToAspectRatioIfValid(getDefaultBounds(),
+ mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
+ false /* useCurrentSize */);
+ }
+
+ /** Returns the default bounds. */
+ public Rect getDefaultBounds() {
+ return getDefaultBounds(INVALID_SNAP_FRACTION, null /* size */);
+ }
+
/** Returns the destination bounds to place the PIP window on entry. */
public Rect getEntryDestinationBounds() {
final PipBoundsState.PipReentryState reentryState = mPipBoundsState.getReentryState();
@@ -135,23 +129,17 @@ public class PipBoundsHandler {
final Rect destinationBounds = shouldRestoreReentryBounds
? getDefaultBounds(reentryState.getSnapFraction(), reentryState.getSize())
- : getDefaultBounds(INVALID_SNAP_FRACTION, null /* size */);
+ : getDefaultBounds();
- if (isValidPictureInPictureAspectRatio(mPipBoundsState.getAspectRatio())) {
- transformBoundsToAspectRatio(destinationBounds, mPipBoundsState.getAspectRatio(),
- false /* useCurrentMinEdgeSize */, shouldRestoreReentryBounds);
- }
- return destinationBounds;
+ return transformBoundsToAspectRatioIfValid(destinationBounds,
+ mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
+ shouldRestoreReentryBounds);
}
/** Returns the current bounds adjusted to the new aspect ratio, if valid. */
public Rect getAdjustedDestinationBounds(Rect currentBounds, float newAspectRatio) {
- final Rect destinationBounds = new Rect(currentBounds);
- if (isValidPictureInPictureAspectRatio(newAspectRatio)) {
- transformBoundsToAspectRatio(destinationBounds, newAspectRatio,
- true /* useCurrentMinEdgeSize */, false /* isReentryBounds */);
- }
- return destinationBounds;
+ return transformBoundsToAspectRatioIfValid(currentBounds, newAspectRatio,
+ true /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
}
public float getDefaultAspectRatio() {
@@ -159,94 +147,6 @@ public class PipBoundsHandler {
}
/**
- * Updatest the display info and display layout on rotation change. This is needed even when we
- * aren't in PIP because the rotation layout is used to calculate the proper insets for the
- * next enter animation into PIP.
- */
- public void onDisplayRotationChangedNotInPip(Context context, int toRotation) {
- // Update the display layout, note that we have to do this on every rotation even if we
- // aren't in PIP since we need to update the display layout to get the right resources
- mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
-
- // Populate the new {@link #mDisplayInfo}.
- // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
- // therefore, the width/height may require a swap first.
- // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
- mPipBoundsState.setDisplayRotation(toRotation);
- updateDisplayInfoIfNeeded();
- }
-
- /**
- * Updates the display info, calculating and returning the new stack and movement bounds in the
- * new orientation of the device if necessary.
- *
- * @return {@code true} if internal {@link DisplayInfo} is rotated, {@code false} otherwise.
- */
- public boolean onDisplayRotationChanged(Context context, Rect outBounds, Rect oldBounds,
- Rect outInsetBounds,
- int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) {
- // Bail early if the event is not sent to current {@link #mDisplayInfo}
- if ((displayId != mPipBoundsState.getDisplayInfo().displayId)
- || (fromRotation == toRotation)) {
- return false;
- }
-
- // Bail early if the pinned task is staled.
- final RootTaskInfo pinnedTaskInfo;
- try {
- pinnedTaskInfo = ActivityTaskManager.getService()
- .getRootTaskInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
- if (pinnedTaskInfo == null) return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get RootTaskInfo for pinned task", e);
- return false;
- }
-
- // Calculate the snap fraction of the current stack along the old movement bounds
- final Rect postChangeStackBounds = new Rect(oldBounds);
- final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds,
- getMovementBounds(postChangeStackBounds), mPipBoundsState.getStashedState());
-
- // Update the display layout
- mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
-
- // Populate the new {@link #mDisplayInfo}.
- // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
- // therefore, the width/height may require a swap first.
- // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
- mPipBoundsState.getDisplayInfo().rotation = toRotation;
- updateDisplayInfoIfNeeded();
-
- // Calculate the stack bounds in the new orientation based on same fraction along the
- // rotated movement bounds.
- final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
- false /* adjustForIme */);
- mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
- snapFraction, mPipBoundsState.getStashedState(), mPipBoundsState.getStashOffset(),
- mPipBoundsState.getDisplayBounds());
-
- getInsetBounds(outInsetBounds);
- outBounds.set(postChangeStackBounds);
- t.setBounds(pinnedTaskInfo.token, outBounds);
- return true;
- }
-
- private void updateDisplayInfoIfNeeded() {
- final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
- final boolean updateNeeded;
- if ((displayInfo.rotation == ROTATION_0) || (displayInfo.rotation == ROTATION_180)) {
- updateNeeded = (displayInfo.logicalWidth > displayInfo.logicalHeight);
- } else {
- updateNeeded = (displayInfo.logicalWidth < displayInfo.logicalHeight);
- }
- if (updateNeeded) {
- final int newLogicalHeight = displayInfo.logicalWidth;
- displayInfo.logicalWidth = displayInfo.logicalHeight;
- displayInfo.logicalHeight = newLogicalHeight;
- }
- }
-
- /**
* @return whether the given {@param aspectRatio} is valid.
*/
private boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
@@ -254,6 +154,16 @@ public class PipBoundsHandler {
&& Float.compare(aspectRatio, mMaxAspectRatio) <= 0;
}
+ private Rect transformBoundsToAspectRatioIfValid(Rect bounds, float aspectRatio,
+ boolean useCurrentMinEdgeSize, boolean useCurrentSize) {
+ final Rect destinationBounds = new Rect(bounds);
+ if (isValidPictureInPictureAspectRatio(aspectRatio)) {
+ transformBoundsToAspectRatio(destinationBounds, aspectRatio,
+ useCurrentMinEdgeSize, useCurrentSize);
+ }
+ return destinationBounds;
+ }
+
/**
* Set the current bounds (or the default bounds if there are no current bounds) with the
* specified aspect ratio.
@@ -273,7 +183,7 @@ public class PipBoundsHandler {
final int minEdgeSize = useCurrentMinEdgeSize ? mPipBoundsState.getMinEdgeSize()
: defaultMinEdgeSize;
// Use the existing size but adjusted to the aspect ratio and min edge size.
- size = mSnapAlgorithm.getSizeForAspectRatio(
+ size = getSizeForAspectRatio(
new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
} else {
if (overrideMinSize != null) {
@@ -283,7 +193,7 @@ public class PipBoundsHandler {
} else {
// Calculate the default size using the display size and default min edge size.
final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
- size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
+ size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
displayInfo.logicalWidth, displayInfo.logicalHeight);
}
}
@@ -328,7 +238,7 @@ public class PipBoundsHandler {
defaultSize = adjustSizeToAspectRatio(overrideMinSize, mDefaultAspectRatio);
} else {
// Calculate the default size using the display size and default min edge size.
- defaultSize = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
+ defaultSize = getSizeForAspectRatio(mDefaultAspectRatio,
mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight);
}
Gravity.apply(mDefaultStackGravity, defaultSize.getWidth(), defaultSize.getHeight(),
@@ -343,7 +253,7 @@ public class PipBoundsHandler {
/**
* Populates the bounds on the screen that the PIP can be visible in.
*/
- protected void getInsetBounds(Rect outRect) {
+ public void getInsetBounds(Rect outRect) {
final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
Rect insets = mPipBoundsState.getDisplayLayout().stableInsets();
outRect.set(insets.left + mScreenEdgeInsets.x,
@@ -353,29 +263,44 @@ public class PipBoundsHandler {
}
/**
- * @return the movement bounds for the given {@param stackBounds} and the current state of the
+ * @return the movement bounds for the given stackBounds and the current state of the
* controller.
*/
- private Rect getMovementBounds(Rect stackBounds) {
+ public Rect getMovementBounds(Rect stackBounds) {
return getMovementBounds(stackBounds, true /* adjustForIme */);
}
/**
- * @return the movement bounds for the given {@param stackBounds} and the current state of the
+ * @return the movement bounds for the given stackBounds and the current state of the
* controller.
*/
- private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
+ public Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
final Rect movementBounds = new Rect();
getInsetBounds(movementBounds);
// Apply the movement bounds adjustments based on the current state.
- mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
+ getMovementBounds(stackBounds, movementBounds, movementBounds,
(adjustForIme && mPipBoundsState.isImeShowing())
? mPipBoundsState.getImeHeight() : 0);
+
return movementBounds;
}
/**
+ * Adjusts movementBoundsOut so that it is the movement bounds for the given stackBounds.
+ */
+ public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut,
+ int bottomOffset) {
+ // Adjust the right/bottom to ensure the stack bounds never goes offscreen
+ movementBoundsOut.set(insetBounds);
+ movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right
+ - stackBounds.width());
+ movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom
+ - stackBounds.height());
+ movementBoundsOut.bottom -= bottomOffset;
+ }
+
+ /**
* @return the default snap fraction to apply instead of the default gravity when calculating
* the default stack bounds when first entering PiP.
*/
@@ -403,6 +328,62 @@ public class PipBoundsHandler {
}
/**
+ * @return the size of the PiP at the given aspectRatio, ensuring that the minimum edge
+ * is at least minEdgeSize.
+ */
+ public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth,
+ int displayHeight) {
+ final int smallestDisplaySize = Math.min(displayWidth, displayHeight);
+ final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent);
+
+ final int width;
+ final int height;
+ if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) {
+ // Beyond these points, we can just use the min size as the shorter edge
+ if (aspectRatio <= 1) {
+ // Portrait, width is the minimum size
+ width = minSize;
+ height = Math.round(width / aspectRatio);
+ } else {
+ // Landscape, height is the minimum size
+ height = minSize;
+ width = Math.round(height * aspectRatio);
+ }
+ } else {
+ // Within these points, we ensure that the bounds fit within the radius of the limits
+ // at the points
+ final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
+ final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
+ height = (int) Math.round(Math.sqrt((radius * radius)
+ / (aspectRatio * aspectRatio + 1)));
+ width = Math.round(height * aspectRatio);
+ }
+ return new Size(width, height);
+ }
+
+ /**
+ * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the
+ * minimum edge is at least minEdgeSize.
+ */
+ public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) {
+ final int smallestSize = Math.min(size.getWidth(), size.getHeight());
+ final int minSize = (int) Math.max(minEdgeSize, smallestSize);
+
+ final int width;
+ final int height;
+ if (aspectRatio <= 1) {
+ // Portrait, width is the minimum size.
+ width = minSize;
+ height = Math.round(width / aspectRatio);
+ } else {
+ // Landscape, height is the minimum size
+ height = minSize;
+ width = Math.round(height * aspectRatio);
+ }
+ return new Size(width, height);
+ }
+
+ /**
* Dumps internal states.
*/
public void dump(PrintWriter pw, String prefix) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 78f7e25ee3fa..ce1139b4264d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -54,6 +54,11 @@ public final class PipBoundsState {
private static final String TAG = PipBoundsState.class.getSimpleName();
private final @NonNull Rect mBounds = new Rect();
+ private final @NonNull Rect mMovementBounds = new Rect();
+ private final @NonNull Rect mNormalBounds = new Rect();
+ private final @NonNull Rect mExpandedBounds = new Rect();
+ private final @NonNull Rect mNormalMovementBounds = new Rect();
+ private final @NonNull Rect mExpandedMovementBounds = new Rect();
private final Context mContext;
private float mAspectRatio;
private int mStashedState = STASH_TYPE_NONE;
@@ -92,9 +97,7 @@ public final class PipBoundsState {
.getDimensionPixelSize(R.dimen.pip_stash_offset);
}
- /**
- * Set the current PIP bounds.
- */
+ /** Set the current PIP bounds. */
public void setBounds(@NonNull Rect bounds) {
mBounds.set(bounds);
}
@@ -104,6 +107,53 @@ public final class PipBoundsState {
return new Rect(mBounds);
}
+ /** Returns the current movement bounds. */
+ public Rect getMovementBounds() {
+ return mMovementBounds;
+ }
+
+ /** Set the current normal PIP bounds. */
+ public void setNormalBounds(@NonNull Rect bounds) {
+ mNormalBounds.set(bounds);
+ }
+
+ /** Get the current normal PIP bounds. */
+ @NonNull
+ public Rect getNormalBounds() {
+ return mNormalBounds;
+ }
+
+ /** Set the expanded bounds of PIP. */
+ public void setExpandedBounds(@NonNull Rect bounds) {
+ mExpandedBounds.set(bounds);
+ }
+
+ /** Get the PIP expanded bounds. */
+ @NonNull
+ public Rect getExpandedBounds() {
+ return mExpandedBounds;
+ }
+
+ /** Set the normal movement bounds. */
+ public void setNormalMovementBounds(Rect bounds) {
+ mNormalMovementBounds.set(bounds);
+ }
+
+ /** Returns the normal movement bounds. */
+ public Rect getNormalMovementBounds() {
+ return mNormalMovementBounds;
+ }
+
+ /** Set the expanded movement bounds. */
+ public void setExpandedMovementBounds(Rect bounds) {
+ mExpandedMovementBounds.set(bounds);
+ }
+
+ /** Returns the expanded movement bounds. */
+ public Rect getExpandedMovementBounds() {
+ return mExpandedMovementBounds;
+ }
+
/**
* Dictate where PiP currently should be stashed, if at all.
*/
@@ -389,6 +439,11 @@ public final class PipBoundsState {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
pw.println(innerPrefix + "mBounds=" + mBounds);
+ pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
+ pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
+ pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
+ pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
+ pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
index 71060752df09..0528e4d88374 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
@@ -20,11 +20,9 @@ import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.PointF;
import android.graphics.Rect;
-import android.util.Size;
+
+import com.android.internal.annotations.VisibleForTesting;
/**
* Calculates the snap targets and the snap position for the PIP given a position and a velocity.
@@ -32,19 +30,6 @@ import android.util.Size;
*/
public class PipSnapAlgorithm {
- private final float mDefaultSizePercent;
- private final float mMinAspectRatioForMinSize;
- private final float mMaxAspectRatioForMinSize;
-
- public PipSnapAlgorithm(Context context) {
- Resources res = context.getResources();
- mDefaultSizePercent = res.getFloat(
- com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
- mMaxAspectRatioForMinSize = res.getFloat(
- com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
- mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
- }
-
/**
* Returns a fraction that describes where the PiP bounds is.
* See {@link #getSnapFraction(Rect, Rect, int)}.
@@ -136,81 +121,11 @@ public class PipSnapAlgorithm {
}
/**
- * Adjusts {@param movementBoundsOut} so that it is the movement bounds for the given
- * {@param stackBounds}.
- */
- public void getMovementBounds(Rect stackBounds, Rect insetBounds, Rect movementBoundsOut,
- int bottomOffset) {
- // Adjust the right/bottom to ensure the stack bounds never goes offscreen
- movementBoundsOut.set(insetBounds);
- movementBoundsOut.right = Math.max(insetBounds.left, insetBounds.right -
- stackBounds.width());
- movementBoundsOut.bottom = Math.max(insetBounds.top, insetBounds.bottom -
- stackBounds.height());
- movementBoundsOut.bottom -= bottomOffset;
- }
-
- /**
- * @return the size of the PiP at the given {@param aspectRatio}, ensuring that the minimum edge
- * is at least {@param minEdgeSize}.
- */
- public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth,
- int displayHeight) {
- final int smallestDisplaySize = Math.min(displayWidth, displayHeight);
- final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent);
-
- final int width;
- final int height;
- if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) {
- // Beyond these points, we can just use the min size as the shorter edge
- if (aspectRatio <= 1) {
- // Portrait, width is the minimum size
- width = minSize;
- height = Math.round(width / aspectRatio);
- } else {
- // Landscape, height is the minimum size
- height = minSize;
- width = Math.round(height * aspectRatio);
- }
- } else {
- // Within these points, we ensure that the bounds fit within the radius of the limits
- // at the points
- final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
- final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
- height = (int) Math.round(Math.sqrt((radius * radius) /
- (aspectRatio * aspectRatio + 1)));
- width = Math.round(height * aspectRatio);
- }
- return new Size(width, height);
- }
-
- /**
- * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the
- * minimum edge is at least minEdgeSize.
- */
- public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) {
- final int smallestSize = Math.min(size.getWidth(), size.getHeight());
- final int minSize = (int) Math.max(minEdgeSize, smallestSize);
-
- final int width;
- final int height;
- if (aspectRatio <= 1) {
- // Portrait, width is the minimum size.
- width = minSize;
- height = Math.round(width / aspectRatio);
- } else {
- // Landscape, height is the minimum size
- height = minSize;
- width = Math.round(height * aspectRatio);
- }
- return new Size(width, height);
- }
-
- /**
* Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes
* the new bounds out to {@param boundsOut}.
*/
- public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
+ @VisibleForTesting
+ void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
@PipBoundsState.StashType int stashType) {
int leftEdge = stackBounds.left;
if (stashType == STASH_TYPE_LEFT) {
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 a095fb525d24..7cc2a419354e 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
@@ -134,7 +134,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final Handler mMainHandler;
private final Handler mUpdateHandler;
private final PipBoundsState mPipBoundsState;
- private final PipBoundsHandler mPipBoundsHandler;
+ private final PipBoundsAlgorithm mPipBoundsAlgorithm;
// TODO(b/172286265): Remove dependency on .pip.PHONE.PipMenuActivityController
private final PipMenuActivityController mMenuActivityController;
private final PipAnimationController mPipAnimationController;
@@ -263,7 +263,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private boolean mShouldIgnoreEnteringPipTransition;
public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState,
- @NonNull PipBoundsHandler boundsHandler,
+ @NonNull PipBoundsAlgorithm boundsHandler,
PipMenuActivityController menuActivityController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
Optional<SplitScreen> splitScreenOptional,
@@ -273,7 +273,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mMainHandler = new Handler(Looper.getMainLooper());
mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
mPipBoundsState = pipBoundsState;
- mPipBoundsHandler = boundsHandler;
+ mPipBoundsAlgorithm = boundsHandler;
mMenuActivityController = menuActivityController;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
@@ -340,7 +340,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mShouldIgnoreEnteringPipTransition = true;
sendOnPipTransitionStarted(componentName, TRANSITION_DIRECTION_TO_PIP);
setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo);
- return mPipBoundsHandler.getEntryDestinationBounds();
+ return mPipBoundsAlgorithm.getEntryDestinationBounds();
}
/**
@@ -528,7 +528,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
- final Rect destinationBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
@@ -690,7 +690,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
// Aspect ratio changed, re-calculate bounds if valid.
- final Rect destinationBounds = mPipBoundsHandler.getAdjustedDestinationBounds(
+ final Rect destinationBounds = mPipBoundsAlgorithm.getAdjustedDestinationBounds(
mPipBoundsState.getBounds(), mPipBoundsState.getAspectRatio());
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
@@ -705,7 +705,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override
public void onFixedRotationFinished(int displayId) {
if (mShouldDeferEnteringPip && mState.isInPip()) {
- final Rect destinationBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
// schedule a regular animation to ensure all the callbacks are still being sent
enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */);
}
@@ -780,7 +780,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
- final Rect newDestinationBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect newDestinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
if (newDestinationBounds.equals(currentDestinationBounds)) return;
if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
animator.updateEndValue(newDestinationBounds);
@@ -1106,7 +1106,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
return params == null || !params.hasSetAspectRatio()
- ? mPipBoundsHandler.getDefaultAspectRatio()
+ ? mPipBoundsAlgorithm.getDefaultAspectRatio()
: params.getAspectRatio();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 18b6922f3067..f153aa5a1beb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -105,8 +105,8 @@ public class PipAccessibilityInteractionConnection
// R constants are not final so this cannot be put in the switch-case.
if (action == R.id.action_pip_resize) {
- if (mMotionHelper.getBounds().width() == mNormalBounds.width()
- && mMotionHelper.getBounds().height() == mNormalBounds.height()) {
+ if (mPipBoundsState.getBounds().width() == mNormalBounds.width()
+ && mPipBoundsState.getBounds().height() == mNormalBounds.height()) {
setToExpandedBounds();
} else {
setToNormalBounds();
@@ -130,7 +130,7 @@ public class PipAccessibilityInteractionConnection
int newY = arguments.getInt(
AccessibilityNodeInfo.ACTION_ARGUMENT_MOVE_WINDOW_Y);
Rect pipBounds = new Rect();
- pipBounds.set(mMotionHelper.getBounds());
+ pipBounds.set(mPipBoundsState.getBounds());
mTmpBounds.offsetTo(newX, newY);
mMotionHelper.movePip(mTmpBounds);
result = true;
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 8e48229ca4bb..598b5d9b5d30 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
@@ -19,6 +19,8 @@ package com.android.wm.shell.pip.phone;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
@@ -56,9 +58,10 @@ 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.PipBoundsHandler;
+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;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipUtils;
@@ -78,13 +81,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private WindowManagerShellWrapper mWindowManagerShellWrapper;
private PipAppOpsListener mAppOpsListener;
private PipMediaController mMediaController;
- private PipBoundsHandler mPipBoundsHandler;
+ private PipBoundsAlgorithm mPipBoundsAlgorithm;
private PipBoundsState mPipBoundsState;
private PipTouchHandler mTouchHandler;
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final Rect mTmpInsetBounds = new Rect();
- private final Rect mTmpNormalBounds = new Rect();
protected final Rect mReentryBounds = new Rect();
private boolean mIsInFixedRotation;
@@ -103,20 +105,20 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isDeferringEnterPipAnimation()) {
// Skip if we aren't in PIP or haven't actually entered PIP yet. We still need to update
// the display layout in the bounds handler in this case.
- mPipBoundsHandler.onDisplayRotationChangedNotInPip(mContext, toRotation);
+ onDisplayRotationChangedNotInPip(mContext, toRotation);
return;
}
// If there is an animation running (ie. from a shelf offset), then ensure that we calculate
// the bounds for the next orientation using the destination bounds of the animation
// TODO: Technically this should account for movement animation bounds as well
Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds();
- final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mContext,
- mTmpNormalBounds, currentBounds, mTmpInsetBounds, displayId, fromRotation,
- toRotation, t);
+ final boolean changed = onDisplayRotationChanged(mContext,
+ mPipBoundsState.getNormalBounds(), currentBounds, mTmpInsetBounds, displayId,
+ fromRotation, toRotation, t);
if (changed) {
// If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the
// movement bounds
- mTouchHandler.adjustBoundsForRotation(mTmpNormalBounds,
+ mTouchHandler.adjustBoundsForRotation(mPipBoundsState.getNormalBounds(),
mPipBoundsState.getBounds(), mTmpInsetBounds);
// The bounds are being applied to a specific snap fraction, so reset any known offsets
@@ -131,7 +133,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mTouchHandler.onImeVisibilityChanged(false, 0);
}
- updateMovementBounds(mTmpNormalBounds, true /* fromRotation */,
+ updateMovementBounds(mPipBoundsState.getNormalBounds(), true /* fromRotation */,
false /* fromImeAdjustment */, false /* fromShelfAdjustment */, t);
}
};
@@ -204,7 +206,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
@Override
public void onConfigurationChanged() {
mMainExecutor.execute(() -> {
- mPipBoundsHandler.onConfigurationChanged(mContext);
+ mPipBoundsAlgorithm.onConfigurationChanged(mContext);
mTouchHandler.onConfigurationChanged();
mPipBoundsState.onConfigurationChanged();
});
@@ -224,7 +226,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
protected PipController(Context context,
DisplayController displayController,
PipAppOpsListener pipAppOpsListener,
- PipBoundsHandler pipBoundsHandler,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
@NonNull PipBoundsState pipBoundsState,
PipMediaController pipMediaController,
PipMenuActivityController pipMenuActivityController,
@@ -243,7 +245,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mContext = context;
mWindowManagerShellWrapper = windowManagerShellWrapper;
mDisplayController = displayController;
- mPipBoundsHandler = pipBoundsHandler;
+ mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mPipTaskOrganizer = pipTaskOrganizer;
mMainExecutor = mainExecutor;
@@ -437,7 +439,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
PictureInPictureParams pictureInPictureParams,
int launcherRotation, int shelfHeight) {
setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
- mPipBoundsHandler.onDisplayRotationChangedNotInPip(mContext, launcherRotation);
+ onDisplayRotationChangedNotInPip(mContext, launcherRotation);
return mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
pictureInPictureParams);
}
@@ -452,7 +454,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
if (isOutPipDirection(direction)) {
// Exiting PIP, save the reentry bounds to restore to when re-entering.
updateReentryBounds(pipBounds);
- final float snapFraction = mPipBoundsHandler.getSnapFraction(mReentryBounds);
+ final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mReentryBounds);
mPipBoundsState.saveReentryState(mReentryBounds, snapFraction);
}
// Disable touches while the animation is running
@@ -467,8 +469,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
*/
public void updateReentryBounds(Rect bounds) {
final Rect reentryBounds = mTouchHandler.getUserResizeBounds();
- float snapFraction = mPipBoundsHandler.getSnapFraction(bounds);
- mPipBoundsHandler.applySnapFraction(reentryBounds, snapFraction);
+ float snapFraction = mPipBoundsAlgorithm.getSnapFraction(bounds);
+ mPipBoundsAlgorithm.applySnapFraction(reentryBounds, snapFraction);
mReentryBounds.set(reentryBounds);
}
@@ -506,23 +508,118 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
// passing to mTouchHandler/mPipTaskOrganizer
final Rect outBounds = new Rect(toBounds);
mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
- mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
- outBounds);
+
+ mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
+ mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds());
+ if (outBounds.isEmpty()) {
+ outBounds.set(mPipBoundsAlgorithm.getDefaultBounds());
+ }
+
// mTouchHandler would rely on the bounds populated from mPipTaskOrganizer
mPipTaskOrganizer.onMovementBoundsChanged(outBounds, fromRotation, fromImeAdjustment,
fromShelfAdjustment, wct);
- mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
+ mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBoundsState.getNormalBounds(),
outBounds, fromImeAdjustment, fromShelfAdjustment,
mTmpDisplayInfo.rotation);
}
+ /**
+ * Updates the display info and display layout on rotation change. This is needed even when we
+ * aren't in PIP because the rotation layout is used to calculate the proper insets for the
+ * next enter animation into PIP.
+ */
+ private void onDisplayRotationChangedNotInPip(Context context, int toRotation) {
+ // Update the display layout, note that we have to do this on every rotation even if we
+ // aren't in PIP since we need to update the display layout to get the right resources
+ mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
+
+ // Populate the new {@link #mDisplayInfo}.
+ // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
+ // therefore, the width/height may require a swap first.
+ // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
+ mPipBoundsState.setDisplayRotation(toRotation);
+ updateDisplayInfoIfNeeded();
+ }
+
+ /**
+ * Updates the display info, calculating and returning the new stack and movement bounds in the
+ * new orientation of the device if necessary.
+ *
+ * @return {@code true} if internal {@link DisplayInfo} is rotated, {@code false} otherwise.
+ */
+ public boolean onDisplayRotationChanged(Context context, Rect outBounds, Rect oldBounds,
+ Rect outInsetBounds,
+ int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) {
+ // Bail early if the event is not sent to current {@link #mDisplayInfo}
+ if ((displayId != mPipBoundsState.getDisplayInfo().displayId)
+ || (fromRotation == toRotation)) {
+ return false;
+ }
+
+ // Bail early if the pinned task is staled.
+ final ActivityTaskManager.RootTaskInfo pinnedTaskInfo;
+ try {
+ pinnedTaskInfo = ActivityTaskManager.getService()
+ .getRootTaskInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
+ if (pinnedTaskInfo == null) return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get RootTaskInfo for pinned task", e);
+ return false;
+ }
+ final PipSnapAlgorithm pipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
+
+ // Calculate the snap fraction of the current stack along the old movement bounds
+ final Rect postChangeStackBounds = new Rect(oldBounds);
+ final float snapFraction = pipSnapAlgorithm.getSnapFraction(postChangeStackBounds,
+ mPipBoundsAlgorithm.getMovementBounds(postChangeStackBounds),
+ mPipBoundsState.getStashedState());
+
+ // Update the display layout
+ mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
+
+ // Populate the new {@link #mDisplayInfo}.
+ // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
+ // therefore, the width/height may require a swap first.
+ // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
+ mPipBoundsState.getDisplayInfo().rotation = toRotation;
+ updateDisplayInfoIfNeeded();
+
+ // Calculate the stack bounds in the new orientation based on same fraction along the
+ // rotated movement bounds.
+ final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds(
+ postChangeStackBounds, false /* adjustForIme */);
+ pipSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
+ snapFraction, mPipBoundsState.getStashedState(), mPipBoundsState.getStashOffset(),
+ mPipBoundsState.getDisplayBounds());
+
+ mPipBoundsAlgorithm.getInsetBounds(outInsetBounds);
+ outBounds.set(postChangeStackBounds);
+ t.setBounds(pinnedTaskInfo.token, outBounds);
+ return true;
+ }
+
+ private void updateDisplayInfoIfNeeded() {
+ final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
+ final boolean updateNeeded;
+ if ((displayInfo.rotation == ROTATION_0) || (displayInfo.rotation == ROTATION_180)) {
+ updateNeeded = (displayInfo.logicalWidth > displayInfo.logicalHeight);
+ } else {
+ updateNeeded = (displayInfo.logicalWidth < displayInfo.logicalHeight);
+ }
+ if (updateNeeded) {
+ final int newLogicalHeight = displayInfo.logicalWidth;
+ displayInfo.logicalWidth = displayInfo.logicalHeight;
+ displayInfo.logicalHeight = newLogicalHeight;
+ }
+ }
+
@Override
public void dump(PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
mMenuController.dump(pw, innerPrefix);
mTouchHandler.dump(pw, innerPrefix);
- mPipBoundsHandler.dump(pw, innerPrefix);
+ mPipBoundsAlgorithm.dump(pw, innerPrefix);
mPipTaskOrganizer.dump(pw, innerPrefix);
mPipBoundsState.dump(pw, innerPrefix);
mPipInputConsumer.dump(pw, innerPrefix);
@@ -533,7 +630,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
*/
@Nullable
public static PipController create(Context context, DisplayController displayController,
- PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler,
+ PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
@@ -543,7 +640,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
return null;
}
- return new PipController(context, displayController, pipAppOpsListener, pipBoundsHandler,
+ return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
pipBoundsState, pipMediaController, pipMenuActivityController, pipTaskOrganizer,
pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index d7b56ae74b38..8fa944886905 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -75,9 +75,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
- /** The bounds within which PIP's top-left coordinate is allowed to move. */
- private final Rect mMovementBounds = new Rect();
-
/** The region that all of PIP must stay within. */
private final Rect mFloatingAllowedArea = new Rect();
@@ -339,23 +336,20 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
}
/** Sets the movement bounds to use to constrain PIP position animations. */
- void setCurrentMovementBounds(Rect movementBounds) {
- mMovementBounds.set(movementBounds);
+ void onMovementBoundsChanged() {
rebuildFlingConfigs();
// The movement bounds represent the area within which we can move PIP's top-left position.
// The allowed area for all of PIP is those bounds plus PIP's width and height.
- mFloatingAllowedArea.set(mMovementBounds);
+ mFloatingAllowedArea.set(mPipBoundsState.getMovementBounds());
mFloatingAllowedArea.right += getBounds().width();
mFloatingAllowedArea.bottom += getBounds().height();
}
/**
* @return the PiP bounds.
- *
- * TODO(b/169373982): can be private, outside callers can use PipBoundsState directly.
*/
- Rect getBounds() {
+ private Rect getBounds() {
return mPipBoundsState.getBounds();
}
@@ -395,10 +389,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
final float leftEdge = isStash
? mPipBoundsState.getStashOffset() - mPipBoundsState.getBounds().width()
- : mMovementBounds.left;
+ : mPipBoundsState.getMovementBounds().left;
final float rightEdge = isStash
? mPipBoundsState.getDisplayBounds().right - mPipBoundsState.getStashOffset()
- : mMovementBounds.right;
+ : mPipBoundsState.getMovementBounds().right;
final float xEndValue = velocityX < 0 ? leftEdge : rightEdge;
@@ -433,7 +427,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
// Animate off the bottom of the screen, then dismiss PIP.
mTemporaryBoundsPhysicsAnimator
.spring(FloatProperties.RECT_Y,
- mMovementBounds.bottom + getBounds().height() * 2,
+ mPipBoundsState.getMovementBounds().bottom + getBounds().height() * 2,
0,
mSpringConfig)
.withEndActions(this::dismissPip);
@@ -504,10 +498,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** Set new fling configs whose min/max values respect the given movement bounds. */
private void rebuildFlingConfigs() {
- mFlingConfigX = new PhysicsAnimator.FlingConfig(
- DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right);
- mFlingConfigY = new PhysicsAnimator.FlingConfig(
- DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
+ mFlingConfigX = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
+ mPipBoundsState.getMovementBounds().left,
+ mPipBoundsState.getMovementBounds().right);
+ mFlingConfigY = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION,
+ mPipBoundsState.getMovementBounds().top,
+ mPipBoundsState.getMovementBounds().bottom);
mStashConfigX = new PhysicsAnimator.FlingConfig(
DEFAULT_FRICTION,
mPipBoundsState.getStashOffset() - mPipBoundsState.getBounds().width(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 124683e2ff37..e5a49c430d82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -46,7 +46,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.wm.shell.R;
-import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipUiEventLogger;
@@ -66,7 +66,7 @@ public class PipResizeGestureHandler {
private static final float STARTING_SCALE_FACTOR = 1.0f;
private final Context mContext;
- private final PipBoundsHandler mPipBoundsHandler;
+ private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final PipMotionHelper mMotionHelper;
private final PipBoundsState mPipBoundsState;
private final int mDisplayId;
@@ -108,7 +108,7 @@ public class PipResizeGestureHandler {
private int mCtrlType;
- public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
+ public PipResizeGestureHandler(Context context, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger,
@@ -116,7 +116,7 @@ public class PipResizeGestureHandler {
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = context.getMainExecutor();
- mPipBoundsHandler = pipBoundsHandler;
+ mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mMotionHelper = motionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
@@ -296,7 +296,7 @@ public class PipResizeGestureHandler {
* |_|_| |_|_|
*/
public boolean isWithinTouchRegion(int x, int y) {
- final Rect currentPipBounds = mMotionHelper.getBounds();
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
if (currentPipBounds == null) {
return false;
}
@@ -349,8 +349,8 @@ public class PipResizeGestureHandler {
}
private void setCtrlTypeForPinchToZoom() {
- final Rect currentPipBounds = mMotionHelper.getBounds();
- mLastDownBounds.set(mMotionHelper.getBounds());
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
+ mLastDownBounds.set(mPipBoundsState.getBounds());
Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
mDisplayBounds.set(movementBounds.left,
@@ -372,7 +372,7 @@ public class PipResizeGestureHandler {
}
private void setCtrlType(int x, int y) {
- final Rect currentPipBounds = mMotionHelper.getBounds();
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
mDisplayBounds.set(movementBounds.left,
@@ -413,13 +413,13 @@ public class PipResizeGestureHandler {
float x = ev.getX();
float y = ev.getY();
if (action == MotionEvent.ACTION_DOWN) {
- final Rect currentPipBounds = mMotionHelper.getBounds();
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
mLastResizeBounds.setEmpty();
mAllowGesture = isInValidSysUiState() && isWithinTouchRegion((int) x, (int) y);
if (mAllowGesture) {
setCtrlType((int) x, (int) y);
mDownPoint.set(x, y);
- mLastDownBounds.set(mMotionHelper.getBounds());
+ mLastDownBounds.set(mPipBoundsState.getBounds());
}
if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY())
&& mPipMenuActivityController.isMenuVisible()) {
@@ -446,12 +446,12 @@ public class PipResizeGestureHandler {
mPipMenuActivityController.hideMenuWithoutResize();
mPipMenuActivityController.hideMenu();
}
- final Rect currentPipBounds = mMotionHelper.getBounds();
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
mMinSize.y, mMaxSize, true,
mLastDownBounds.width() > mLastDownBounds.height()));
- mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds,
+ mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds,
mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
true /* useCurrentSize */);
mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
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 36d276822a89..48fa2115305d 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
@@ -48,7 +48,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipUiEventLogger;
@@ -69,7 +69,7 @@ public class PipTouchHandler {
// Allow PIP to resize to a slightly bigger state upon touch
private final boolean mEnableResize;
private final Context mContext;
- private final PipBoundsHandler mPipBoundsHandler;
+ private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final @NonNull PipBoundsState mPipBoundsState;
private final PipUiEventLogger mPipUiEventLogger;
private final PipDismissTargetHandler mPipDismissTargetHandler;
@@ -88,16 +88,8 @@ public class PipTouchHandler {
*/
private boolean mEnableStash = true;
- // The current movement bounds
- private Rect mMovementBounds = new Rect();
-
// The reference inset bounds, used to determine the dismiss fraction
private Rect mInsetBounds = new Rect();
- // The reference bounds used to calculate the normal/expanded target bounds
- private Rect mNormalBounds = new Rect();
- @VisibleForTesting public Rect mNormalMovementBounds = new Rect();
- private Rect mExpandedBounds = new Rect();
- @VisibleForTesting public Rect mExpandedMovementBounds = new Rect();
private int mExpandedShortestEdgeSize;
// Used to workaround an issue where the WM rotation happens before we are notified, allowing
@@ -153,7 +145,7 @@ public class PipTouchHandler {
@Override
public void onPipShowMenu() {
- mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
+ mMenuController.showMenu(MENU_STATE_FULL, mPipBoundsState.getBounds(),
true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle());
}
}
@@ -161,7 +153,7 @@ public class PipTouchHandler {
@SuppressLint("InflateParams")
public PipTouchHandler(Context context,
PipMenuActivityController menuController,
- PipBoundsHandler pipBoundsHandler,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
@NonNull PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
@@ -169,22 +161,24 @@ public class PipTouchHandler {
// Initialize the Pip input consumer
mContext = context;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
- mPipBoundsHandler = pipBoundsHandler;
+ mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mMenuController = menuController;
mMenuController.addListener(new PipMenuListener());
mGesture = new DefaultPipTouchGesture();
mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer,
- mMenuController, mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator);
+ mMenuController, mPipBoundsAlgorithm.getSnapAlgorithm(),
+ floatingContentCoordinator);
mPipResizeGestureHandler =
- new PipResizeGestureHandler(context, pipBoundsHandler, pipBoundsState,
+ new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
mMotionHelper, pipTaskOrganizer, this::getMovementBounds,
this::updateMovementBounds, pipUiEventLogger, menuController);
mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
mMotionHelper, mHandler);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
- () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
- true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
+ () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL,
+ mPipBoundsState.getBounds(), true /* allowMenuTimeout */, willResizeMenu(),
+ shouldShowResizeHandle()),
menuController::hideMenu);
Resources res = context.getResources();
@@ -193,7 +187,7 @@ public class PipTouchHandler {
mFloatingContentCoordinator = floatingContentCoordinator;
mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
- mMotionHelper, pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(),
+ mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler);
mPipUiEventLogger = pipUiEventLogger;
@@ -236,7 +230,7 @@ public class PipTouchHandler {
public void showPictureInPictureMenu() {
// Only show the menu if the user isn't currently interacting with the PiP
if (!mTouchState.isUserInteracting()) {
- mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
+ mMenuController.showMenu(MENU_STATE_FULL, mPipBoundsState.getBounds(),
false /* allowMenuTimeout */, willResizeMenu(),
shouldShowResizeHandle());
}
@@ -272,11 +266,11 @@ public class PipTouchHandler {
updateMovementBounds();
if (direction == TRANSITION_DIRECTION_TO_PIP) {
// Set the initial bounds as the user resize bounds.
- mPipResizeGestureHandler.setUserResizeBounds(mMotionHelper.getBounds());
+ mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
}
if (mShowPipMenuOnAnimationEnd) {
- mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
+ mMenuController.showMenu(MENU_STATE_CLOSE, mPipBoundsState.getBounds(),
true /* allowMenuTimeout */, false /* willResizeMenu */,
shouldShowResizeHandle());
mShowPipMenuOnAnimationEnd = false;
@@ -313,9 +307,9 @@ public class PipTouchHandler {
public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) {
final Rect toMovementBounds = new Rect();
- mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds,
- toMovementBounds, 0);
- final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
+ mPipBoundsAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0);
+ final int prevBottom = mPipBoundsState.getMovementBounds().bottom
+ - mMovementBoundsExtraOffsets;
if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) {
outBounds.offsetTo(outBounds.left, toMovementBounds.bottom);
}
@@ -343,31 +337,33 @@ public class PipTouchHandler {
}
// Re-calculate the expanded bounds
- mNormalBounds.set(normalBounds);
Rect normalMovementBounds = new Rect();
- mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mNormalBounds, insetBounds,
+ mPipBoundsAlgorithm.getMovementBounds(normalBounds, insetBounds,
normalMovementBounds, bottomOffset);
- if (mMovementBounds.isEmpty()) {
+ if (mPipBoundsState.getMovementBounds().isEmpty()) {
// mMovementBounds is not initialized yet and a clean movement bounds without
// bottom offset shall be used later in this function.
- mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds,
- mMovementBounds, 0 /* bottomOffset */);
+ mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds,
+ mPipBoundsState.getMovementBounds(), 0 /* bottomOffset */);
}
// Calculate the expanded size
float aspectRatio = (float) normalBounds.width() / normalBounds.height();
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
- Size expandedSize = mPipBoundsHandler.getSnapAlgorithm().getSizeForAspectRatio(aspectRatio,
- mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
- mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
+ Size expandedSize = mPipBoundsAlgorithm.getSizeForAspectRatio(
+ aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
+ mPipBoundsState.setExpandedBounds(
+ new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight()));
Rect expandedMovementBounds = new Rect();
- mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mExpandedBounds, insetBounds,
- expandedMovementBounds, bottomOffset);
+ mPipBoundsAlgorithm.getMovementBounds(
+ mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
+ bottomOffset);
- mPipResizeGestureHandler.updateMinSize(mNormalBounds.width(), mNormalBounds.height());
- mPipResizeGestureHandler.updateMaxSize(mExpandedBounds.width(), mExpandedBounds.height());
+ mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
+ mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
+ mPipBoundsState.getExpandedBounds().height());
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
@@ -384,9 +380,10 @@ public class PipTouchHandler {
} else {
final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu();
final Rect toMovementBounds = new Rect();
- mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds,
+ mPipBoundsAlgorithm.getMovementBounds(curBounds, insetBounds,
toMovementBounds, mIsImeShowing ? mImeHeight : 0);
- final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
+ final int prevBottom = mPipBoundsState.getMovementBounds().bottom
+ - mMovementBoundsExtraOffsets;
// This is to handle landscape fullscreen IMEs, don't apply the extra offset in this
// case
final int toBottom = toMovementBounds.bottom < toMovementBounds.top
@@ -394,8 +391,8 @@ public class PipTouchHandler {
: toMovementBounds.bottom - extraOffset;
if (isExpanded) {
- curBounds.set(mExpandedBounds);
- mPipBoundsHandler.getSnapAlgorithm().applySnapFraction(curBounds,
+ curBounds.set(mPipBoundsState.getExpandedBounds());
+ mPipBoundsAlgorithm.getSnapAlgorithm().applySnapFraction(curBounds,
toMovementBounds, mSavedSnapFraction);
}
@@ -415,19 +412,21 @@ public class PipTouchHandler {
// Update the movement bounds after doing the calculations based on the old movement bounds
// above
- mNormalMovementBounds.set(normalMovementBounds);
- mExpandedMovementBounds.set(expandedMovementBounds);
+ mPipBoundsState.setNormalMovementBounds(normalMovementBounds);
+ mPipBoundsState.setExpandedMovementBounds(expandedMovementBounds);
mDisplayRotation = displayRotation;
mInsetBounds.set(insetBounds);
updateMovementBounds();
mMovementBoundsExtraOffsets = extraOffset;
- mConnection.onMovementBoundsChanged(mNormalBounds, mExpandedBounds, mNormalMovementBounds,
- mExpandedMovementBounds);
+ mConnection.onMovementBoundsChanged(normalBounds, mPipBoundsState.getExpandedBounds(),
+ mPipBoundsState.getNormalMovementBounds(),
+ mPipBoundsState.getExpandedMovementBounds());
// If we have a deferred resize, apply it now
if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
- mNormalMovementBounds, mMovementBounds, true /* immediate */);
+ mPipBoundsState.getNormalMovementBounds(), mPipBoundsState.getMovementBounds(),
+ true /* immediate */);
mSavedSnapFraction = -1f;
mDeferResizeToNormalBoundsUntilRotation = -1;
}
@@ -447,7 +446,7 @@ public class PipTouchHandler {
}
private void onAccessibilityShowMenu() {
- mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
+ mMenuController.showMenu(MENU_STATE_FULL, mPipBoundsState.getBounds(),
true /* allowMenuTimeout */, willResizeMenu(),
shouldShowResizeHandle());
}
@@ -530,7 +529,7 @@ public class PipTouchHandler {
// Let's not enable menu show/hide for a11y services.
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
mTouchState.removeHoverExitTimeoutCallback();
- mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
+ mMenuController.showMenu(MENU_STATE_FULL, mPipBoundsState.getBounds(),
false /* allowMenuTimeout */, false /* willResizeMenu */,
shouldShowResizeHandle());
}
@@ -651,17 +650,18 @@ public class PipTouchHandler {
}
private void animateToExpandedState(Runnable callback) {
- Rect expandedBounds = new Rect(mExpandedBounds);
+ Rect expandedBounds = new Rect(mPipBoundsState.getExpandedBounds());
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
- mMovementBounds, mExpandedMovementBounds, callback);
+ mPipBoundsState.getMovementBounds(), mPipBoundsState.getExpandedMovementBounds(),
+ callback);
}
private void animateToUnexpandedState(Rect restoreBounds) {
Rect restoredMovementBounds = new Rect();
- mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(restoreBounds,
+ mPipBoundsAlgorithm.getMovementBounds(restoreBounds,
mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
- restoredMovementBounds, mMovementBounds, false /* immediate */);
+ restoredMovementBounds, mPipBoundsState.getMovementBounds(), false /* immediate */);
mSavedSnapFraction = -1f;
}
@@ -687,13 +687,6 @@ public class PipTouchHandler {
mMotionHelper = pipMotionHelper;
}
- /**
- * @return the unexpanded bounds.
- */
- public Rect getNormalBounds() {
- return mNormalBounds;
- }
-
Rect getUserResizeBounds() {
return mPipResizeGestureHandler.getUserResizeBounds();
}
@@ -716,9 +709,10 @@ public class PipTouchHandler {
Rect bounds = getPossiblyAnimatingBounds();
mDelta.set(0f, 0f);
mStartPosition.set(bounds.left, bounds.top);
- mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
+ mMovementWithinDismiss = touchState.getDownTouchPosition().y
+ >= mPipBoundsState.getMovementBounds().bottom;
mMotionHelper.setSpringingToTouch(false);
- mDownSavedFraction = mPipBoundsHandler.getSnapFraction(mPipBoundsState.getBounds());
+ mDownSavedFraction = mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds());
// If the menu is still visible then just poke the menu
// so that it will timeout after the user stops touching it
@@ -758,7 +752,7 @@ public class PipTouchHandler {
final PointF curPos = touchState.getLastTouchPosition();
if (mMovementWithinDismiss) {
// Track if movement remains near the bottom edge to identify swipe to dismiss
- mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom;
+ mMovementWithinDismiss = curPos.y >= mPipBoundsState.getMovementBounds().bottom;
}
return true;
}
@@ -779,7 +773,7 @@ public class PipTouchHandler {
if (mMenuState != MENU_STATE_NONE) {
// If the menu is still visible, then just poke the menu so that
// it will timeout after the user stops touching it
- mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(),
+ mMenuController.showMenu(mMenuState, mPipBoundsState.getBounds(),
true /* allowMenuTimeout */, willResizeMenu(),
shouldShowResizeHandle());
}
@@ -803,15 +797,17 @@ public class PipTouchHandler {
} else if (mTouchState.isDoubleTap() && !mPipBoundsState.isStashed()) {
// If using pinch to zoom, double-tap functions as resizing between max/min size
if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
- final boolean toExpand =
- mMotionHelper.getBounds().width() < mExpandedBounds.width()
- && mMotionHelper.getBounds().height() < mExpandedBounds.height();
- mPipResizeGestureHandler.setUserResizeBounds(toExpand ? mExpandedBounds
- : mNormalBounds);
+ final boolean toExpand = mPipBoundsState.getBounds().width()
+ < mPipBoundsState.getExpandedBounds().width()
+ && mPipBoundsState.getBounds().height()
+ < mPipBoundsState.getExpandedBounds().height();
+ mPipResizeGestureHandler.setUserResizeBounds(toExpand
+ ? mPipBoundsState.getExpandedBounds()
+ : mPipBoundsState.getNormalBounds());
if (toExpand) {
animateToExpandedState(null);
} else {
- animateToUnexpandedState(mNormalBounds);
+ animateToUnexpandedState(mPipBoundsState.getNormalBounds());
}
} else {
// Expand to fullscreen if this is a double tap
@@ -823,7 +819,7 @@ public class PipTouchHandler {
if (!mTouchState.isWaitingForDoubleTap()) {
// User has stalled long enough for this not to be a drag or a double tap, just
// expand the menu
- mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
+ mMenuController.showMenu(MENU_STATE_FULL, mPipBoundsState.getBounds(),
true /* allowMenuTimeout */, willResizeMenu(),
shouldShowResizeHandle());
} else {
@@ -868,19 +864,19 @@ public class PipTouchHandler {
* resized.
*/
private void updateMovementBounds() {
- mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mMotionHelper.getBounds(),
- mInsetBounds, mMovementBounds, mIsImeShowing ? mImeHeight : 0);
- mMotionHelper.setCurrentMovementBounds(mMovementBounds);
+ mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
+ mInsetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0);
+ mMotionHelper.onMovementBoundsChanged();
boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
mPipBoundsState.setMinEdgeSize(
isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize
- : mPipBoundsHandler.getDefaultMinSize());
+ : mPipBoundsAlgorithm.getDefaultMinSize());
}
private Rect getMovementBounds(Rect curBounds) {
Rect movementBounds = new Rect();
- mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, mInsetBounds,
+ mPipBoundsAlgorithm.getMovementBounds(curBounds, mInsetBounds,
movementBounds, mIsImeShowing ? mImeHeight : 0);
return movementBounds;
}
@@ -892,8 +888,10 @@ public class PipTouchHandler {
if (!mEnableResize) {
return false;
}
- return mExpandedBounds.width() != mNormalBounds.width()
- || mExpandedBounds.height() != mNormalBounds.height();
+ return mPipBoundsState.getExpandedBounds().width()
+ != mPipBoundsState.getNormalBounds().width()
+ || mPipBoundsState.getExpandedBounds().height()
+ != mPipBoundsState.getNormalBounds().height();
}
/**
@@ -909,11 +907,6 @@ public class PipTouchHandler {
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
- pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
- pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
- pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
- pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
- pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
pw.println(innerPrefix + "mMenuState=" + mMenuState);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
@@ -921,7 +914,7 @@ public class PipTouchHandler {
pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets);
- mPipBoundsHandler.dump(pw, innerPrefix);
+ mPipBoundsAlgorithm.dump(pw, innerPrefix);
mTouchState.dump(pw, innerPrefix);
if (mPipResizeGestureHandler != null) {
mPipResizeGestureHandler.dump(pw, innerPrefix);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
index c23bc1d63c3b..56e97b91c9d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
@@ -52,7 +52,7 @@ 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.PipBoundsHandler;
+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;
@@ -108,7 +108,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private final Context mContext;
private final PipBoundsState mPipBoundsState;
- private final PipBoundsHandler mPipBoundsHandler;
+ private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final PipTaskOrganizer mPipTaskOrganizer;
private final PipMediaController mPipMediaController;
@@ -204,9 +204,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
public void onMovementBoundsChanged(boolean fromImeAdjustment) {
mHandler.post(() -> {
mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
- // Populate the inset / normal bounds from mPipBoundsHandler first.
- mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBounds,
- mDefaultPipBounds);
+
+ mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
+ mPipBounds.set(mPipBoundsAlgorithm.getNormalBounds());
+ if (mDefaultPipBounds.isEmpty()) {
+ mDefaultPipBounds.set(mPipBoundsAlgorithm.getDefaultBounds());
+ }
});
}
@@ -223,7 +226,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
public PipController(Context context,
PipBoundsState pipBoundsState,
- PipBoundsHandler pipBoundsHandler,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
PipTaskOrganizer pipTaskOrganizer,
PipMediaController pipMediaController,
PipNotification pipNotification,
@@ -232,7 +235,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mContext = context;
mPipBoundsState = pipBoundsState;
mPipNotification = pipNotification;
- mPipBoundsHandler = pipBoundsHandler;
+ mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipMediaController = pipMediaController;
// Ensure that we have the display info in case we get calls to update the bounds
// before the listener calls back
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
new file mode 100644
index 000000000000..2c6c7b358e3b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -0,0 +1,2 @@
+# includes OWNERS from parent directories
+natanieljr@google.com
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 8c4f5468906f..0fb43e263d05 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
@@ -72,6 +72,16 @@ fun LayersAssertion.noUncoveredRegions(
}
@JvmOverloads
+fun LayersAssertion.statusBarLayerIsAlwaysVisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
+ this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+ }
+}
+
+@JvmOverloads
fun LayersAssertion.navBarLayerIsAlwaysVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -82,12 +92,42 @@ fun LayersAssertion.navBarLayerIsAlwaysVisible(
}
@JvmOverloads
-fun LayersAssertion.statusBarLayerIsAlwaysVisible(
+fun LayersAssertion.appPairsDividerIsVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
- all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
- this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+ end("appPairsDividerIsVisible", bugId, enabled) {
+ this.showsLayer(FlickerTestBase.APP_PAIRS_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertion.appPairsDividerIsInvisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ end("appPairsDividerIsInVisible", bugId, enabled) {
+ this.hasNotLayer(FlickerTestBase.APP_PAIRS_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertion.dockedStackDividerIsVisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ end("dockedStackDividerIsVisible", bugId, enabled) {
+ this.showsLayer(FlickerTestBase.DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertion.dockedStackDividerIsInvisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ end("dockedStackDividerIsInvisible", bugId, enabled) {
+ this.hasNotLayer(FlickerTestBase.DOCKED_STACK_DIVIDER)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index ef0390fc2a73..3c222e7d8b56 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -20,17 +20,27 @@ import android.content.ComponentName
const val IME_WINDOW_NAME = "InputMethod"
const val PIP_WINDOW_NAME = "PipMenuActivity"
+const val SPLITSCREEN_PRIMARY_WINDOW_NAME = "SplitScreenActivity"
+const val SPLITSCREEN_SECONDARY_WINDOW_NAME = "SplitScreenSecondaryActivity"
-// Test App
+const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
const val TEST_APP_PACKAGE_NAME = "com.android.wm.shell.flicker.testapp"
+
// Test App > Pip Activity
val TEST_APP_PIP_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
TEST_APP_PACKAGE_NAME, ".PipActivity")
const val TEST_APP_PIP_ACTIVITY_LABEL = "PipApp"
const val TEST_APP_PIP_ACTIVITY_WINDOW_NAME = "PipActivity"
+
// Test App > Ime Activity
val TEST_APP_IME_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
TEST_APP_PACKAGE_NAME, ".ImeActivity")
const val TEST_APP_IME_ACTIVITY_LABEL = "ImeApp"
-const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui" \ No newline at end of file
+// Test App > SplitScreen Activity
+val TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
+ TEST_APP_PACKAGE_NAME, ".$SPLITSCREEN_PRIMARY_WINDOW_NAME")
+val TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME: ComponentName = ComponentName.createRelative(
+ TEST_APP_PACKAGE_NAME, ".$SPLITSCREEN_SECONDARY_WINDOW_NAME")
+const val TEST_APP_SPLITSCREEN_PRIMARY_LABEL = "SplitScreenPrimaryApp"
+const val TEST_APP_SPLITSCREEN_SECONDARY_LABEL = "SplitScreenSecondaryApp"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
index 75b55c1e6a9f..54b8fdc83a1f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
@@ -130,5 +130,7 @@ abstract class FlickerTestBase {
const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
+ const val APP_PAIRS_DIVIDER = "AppPairDivider"
+ const val IMAGE_WALLPAPER = "ImageWallpaper"
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
new file mode 100644
index 000000000000..c33dbbc3f53a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.apppairs
+
+import android.os.SystemClock
+import android.util.Log
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.compatibility.common.util.SystemUtil
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.dsl.runWithFlicker
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.TEST_REPETITIONS
+import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
+import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
+import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+import java.io.IOException
+
+/**
+ * Test AppPairs launch.
+ * To run this test: `atest WMShellFlickerTests:AppPairsTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AppPairsTest(
+ rotationName: String,
+ rotation: Int
+) : AppPairsTestBase(rotationName, rotation) {
+ private val appPairsSetup: FlickerBuilder
+ get() = FlickerBuilder(instrumentation).apply {
+ val testLaunchActivity = "launch_appPairs_primary_secondary_activities"
+ withTestName {
+ testLaunchActivity
+ }
+ setup {
+ eachRun {
+ uiDevice.wakeUpAndGoToHomeScreen()
+ primaryApp.open()
+ uiDevice.pressHome()
+ secondaryApp.open()
+ uiDevice.pressHome()
+ updateTaskId()
+ }
+ }
+ teardown {
+ eachRun {
+ executeShellCommand(composePairsCommand(
+ primaryTaskId, secondaryTaskId, false /* pair */))
+ primaryApp.forceStop()
+ secondaryApp.forceStop()
+ }
+ }
+ assertions {
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+ }
+ }
+
+ @Test
+ fun testAppPairs_pairPrimaryAndSecondaryApps() {
+ val testTag = "testAppPaired_pairPrimaryAndSecondary"
+ runWithFlicker(appPairsSetup) {
+ withTestName { testTag }
+ repeat {
+ TEST_REPETITIONS
+ }
+ transitions {
+ // TODO pair apps through normal UX flow
+ executeShellCommand(composePairsCommand(
+ primaryTaskId, secondaryTaskId, true /* pair */))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ assertions {
+ layersTrace {
+ appPairsDividerIsVisible()
+ end("appsEndingBounds", enabled = false) {
+ val entry = this.trace.entries.firstOrNull()
+ ?: throw IllegalStateException("Trace is empty")
+ val dividerRegion = entry.getVisibleBounds(APP_PAIRS_DIVIDER)
+ this.hasVisibleRegion(primaryApp.defaultWindowName,
+ appPairsHelper.getPrimaryBounds(dividerRegion))
+ .and()
+ .hasVisibleRegion(secondaryApp.defaultWindowName,
+ appPairsHelper.getSecondaryBounds(dividerRegion))
+ }
+ }
+ windowManagerTrace {
+ end {
+ showsAppWindow(primaryApp.defaultWindowName)
+ .and()
+ .showsAppWindow(secondaryApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun testAppPairs_unpairPrimaryAndSecondary() {
+ val testTag = "testAppPairs_unpairPrimaryAndSecondary"
+ runWithFlicker(appPairsSetup) {
+ withTestName { testTag }
+ repeat {
+ TEST_REPETITIONS
+ }
+ setup {
+ executeShellCommand(composePairsCommand(
+ primaryTaskId, secondaryTaskId, true /* pair */))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ transitions {
+ // TODO pair apps through normal UX flow
+ executeShellCommand(composePairsCommand(
+ primaryTaskId, secondaryTaskId, false /* pair */))
+ SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ }
+ assertions {
+ layersTrace {
+ appPairsDividerIsInvisible()
+ start("appsStartingBounds", enabled = false) {
+ val entry = this.trace.entries.firstOrNull()
+ ?: throw IllegalStateException("Trace is empty")
+ val dividerRegion = entry.getVisibleBounds(APP_PAIRS_DIVIDER)
+ this.hasVisibleRegion(primaryApp.defaultWindowName,
+ appPairsHelper.getPrimaryBounds(dividerRegion))
+ .and()
+ .hasVisibleRegion(secondaryApp.defaultWindowName,
+ appPairsHelper.getSecondaryBounds(dividerRegion))
+ }
+ end("appsEndingBounds", enabled = false) {
+ this.hasNotLayer(primaryApp.defaultWindowName)
+ .and()
+ .hasNotLayer(secondaryApp.defaultWindowName)
+ }
+ }
+ windowManagerTrace {
+ end {
+ hidesAppWindow(primaryApp.defaultWindowName)
+ .and()
+ .hidesAppWindow(secondaryApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ }
+
+ private fun composePairsCommand(
+ primaryApp: String,
+ secondaryApp: String,
+ pair: Boolean
+ ): String = buildString {
+ // dumpsys activity service SystemUIService WMShell {pair|unpair} ${TASK_ID_1} ${TASK_ID_2}
+ append("dumpsys activity service SystemUIService WMShell ")
+ if (pair) {
+ append("pair ")
+ } else {
+ append("unpair ")
+ }
+ append(primaryApp + " " + secondaryApp)
+ }
+
+ private fun executeShellCommand(cmd: String) {
+ try {
+ SystemUtil.runShellCommand(instrumentation, cmd)
+ } catch (e: IOException) {
+ Log.d("AppPairsTest", "executeShellCommand error!" + e)
+ }
+ }
+
+ private fun updateTaskId() {
+ val primaryAppComponent = primaryApp.openAppIntent.component
+ val secondaryAppComponent = secondaryApp.openAppIntent.component
+ if (primaryAppComponent != null) {
+ primaryTaskId = appPairsHelper.getTaskIdForActivity(
+ primaryAppComponent.packageName, primaryAppComponent.className).toString()
+ }
+ if (secondaryAppComponent != null) {
+ secondaryTaskId = appPairsHelper.getTaskIdForActivity(
+ secondaryAppComponent.packageName, secondaryAppComponent.className).toString()
+ }
+ }
+
+ companion object {
+ var primaryTaskId = ""
+ var secondaryTaskId = ""
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0)
+ return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt
new file mode 100644
index 000000000000..1a4de0a80bec
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.apppairs
+
+import com.android.wm.shell.flicker.NonRotationTestBase
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_LABEL
+import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+
+abstract class AppPairsTestBase(
+ rotationName: String,
+ rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
+ protected val appPairsHelper = AppPairsHelper(instrumentation,
+ TEST_APP_SPLITSCREEN_PRIMARY_LABEL,
+ TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME)
+ protected val primaryApp = SplitScreenHelper(instrumentation,
+ TEST_APP_SPLITSCREEN_PRIMARY_LABEL,
+ TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME)
+ protected val secondaryApp = SplitScreenHelper(instrumentation,
+ TEST_APP_SPLITSCREEN_SECONDARY_LABEL,
+ TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME)
+}
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
new file mode 100644
index 000000000000..3b6fcdbee4be
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.graphics.Region
+import android.system.helpers.ActivityHelper
+import com.android.server.wm.flicker.helpers.WindowUtils
+
+class AppPairsHelper(
+ instrumentation: Instrumentation,
+ activityLabel: String,
+ componentName: ComponentName
+) : BaseAppHelper(
+ instrumentation,
+ activityLabel,
+ componentName
+) {
+ val activityHelper = ActivityHelper.getInstance()
+
+ fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region {
+ val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
+ dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset)
+ return primaryAppBounds
+ }
+
+ fun getSecondaryBounds(dividerBounds: Region): android.graphics.Region {
+ val displayBounds = WindowUtils.displayBounds
+ val secondaryAppBounds = Region(0,
+ dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight)
+ return secondaryAppBounds
+ }
+
+ fun getTaskIdForActivity(pkgName: String, activityName: String): Int {
+ return activityHelper.getTaskIdForActivity(pkgName, activityName)
+ }
+
+ companion object {
+ const val TEST_REPETITIONS = 1
+ const val TIMEOUT_MS = 3_000L
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index 4ab5c6a20005..6f008ce64fb4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -56,6 +56,9 @@ abstract class BaseAppHelper(
hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
}
+ val defaultWindowName: String
+ get() = launcherActivityComponent.className
+
val label: String
get() = context.packageManager.run {
getApplicationLabel(getApplicationInfo(packageName, 0)).toString()
@@ -74,7 +77,9 @@ abstract class BaseAppHelper(
return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS)
}
- fun forceStop() = activityManager?.forceStopPackage(packageName)
+ fun forceStop() {
+ activityManager?.forceStopPackage(packageName)
+ }
override fun getOpenAppIntent(): Intent {
val intent = Intent()
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 42686e7e87d7..532b3de6c99e 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
@@ -29,6 +29,7 @@ import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_COMPONENT_NAME
import com.android.wm.shell.flicker.TEST_APP_PIP_ACTIVITY_LABEL
import org.junit.Assert.assertNotNull
+import org.junit.Assert.fail
class PipAppHelper(
instrumentation: Instrumentation
@@ -46,11 +47,12 @@ class PipAppHelper(
it.packageName == packageName
}
+ fun clickButton(resourceId: String) =
+ uiDevice.findObject(By.res(packageName, resourceId))?.click()
+ ?: fail("$resourceId button is not found")
+
fun clickEnterPipButton() {
- val enterPipButton = uiDevice.findObject(By.res(packageName, "enter_pip"))
- assertNotNull("Pip button not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)", enterPipButton)
- enterPipButton.click()
+ clickButton("enter_pip")
// TODO(b/172321238): remove this check once hasPipWindow is fixed on TVs
if (!isTelevision) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
new file mode 100644
index 000000000000..10daa675ce36
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.graphics.Region
+import android.os.SystemClock
+import com.android.server.wm.flicker.helpers.WindowUtils
+
+class SplitScreenHelper(
+ instrumentation: Instrumentation,
+ activityLabel: String,
+ componentName: ComponentName
+) : BaseAppHelper(
+ instrumentation,
+ activityLabel,
+ componentName
+) {
+
+ /**
+ * Reopens the first device window from the list of recent apps (overview)
+ */
+ fun reopenAppFromOverview() {
+ val x = uiDevice.displayWidth / 2
+ val y = uiDevice.displayHeight / 2
+ uiDevice.click(x, y)
+ // Wait for animation to complete.
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
+ fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region {
+ val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
+ dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset)
+ return primaryAppBounds
+ }
+
+ fun getSecondaryBounds(dividerBounds: Region): android.graphics.Region {
+ val displayBounds = WindowUtils.displayBounds
+ val secondaryAppBounds = Region(0,
+ dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight)
+ return secondaryAppBounds
+ }
+
+ companion object {
+ const val TEST_REPETITIONS = 1
+ const val TIMEOUT_MS = 3_000L
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index d343f2a7093b..3813d4dc3700 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
@@ -76,12 +76,12 @@ class PipKeyboardTest(
}
teardown {
test {
- keyboardApp.exit()
+ keyboardApp.forceStop()
if (device.hasPipWindow()) {
device.closePipWindow()
}
- testApp.exit()
+ testApp.forceStop()
this.setRotation(Surface.ROTATION_0)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
new file mode 100644
index 000000000000..70425a343c16
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip.tv
+
+import android.graphics.Rect
+import android.util.Rational
+import androidx.test.filters.RequiresDevice
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip Menu on TV.
+ * To run this test: `atest WMShellFlickerTests:TvPipBasicTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+class TvPipBasicTest(
+ private val radioButtonId: String,
+ private val pipWindowRatio: Rational?
+) : TvPipTestBase() {
+
+ @Test
+ fun enterPip_openMenu_pressBack_closePip() {
+ // Launch test app
+ testApp.launchViaIntent()
+
+ // Set up ratio and enter Pip
+ testApp.clickButton(radioButtonId)
+ testApp.clickEnterPipButton()
+
+ val actualRatio: Float = testApp.ui?.visibleBounds?.ratio
+ ?: fail("Application UI not found")
+ pipWindowRatio?.let { expectedRatio ->
+ assertEquals("Wrong Pip window ratio", expectedRatio.toFloat(), actualRatio)
+ }
+
+ // Pressing the Window key should bring up Pip menu
+ uiDevice.pressWindowKey()
+ uiDevice.waitForTvPipMenu() ?: fail("Pip menu should have been shown")
+
+ // Pressing the Back key should close the Pip menu
+ uiDevice.pressBack()
+ assertTrue("Pip menu should have closed", uiDevice.waitForTvPipMenuToClose())
+
+ // Make sure Pip Window ration remained the same after Pip menu was closed
+ testApp.ui?.visibleBounds?.let { newBounds ->
+ assertEquals("Pip window ratio has changed", actualRatio, newBounds.ratio)
+ } ?: fail("Application UI not found")
+
+ // Close Pip
+ testApp.closePipWindow()
+ }
+
+ private val Rect.ratio: Float
+ get() = width().toFloat() / height()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any?>> {
+ infix fun Int.to(denominator: Int) = Rational(this, denominator)
+ return listOf(
+ arrayOf("ratio_default", null),
+ arrayOf("ratio_square", 1 to 1),
+ arrayOf("ratio_wide", 2 to 1),
+ arrayOf("ratio_tall", 1 to 2)
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
index 728cc510a79b..871732cf7460 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip.tv
+import android.graphics.Rect
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.UiObject2
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
@@ -23,20 +24,20 @@ import com.android.wm.shell.flicker.wait
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
/**
* Test Pip Menu on TV.
* To run this test: `atest WMShellFlickerTests:TvPipMenuTests`
*/
@RequiresDevice
-@RunWith(Parameterized::class)
-class TvPipMenuTests(rotationName: String, rotation: Int)
- : TvPipTestBase(rotationName, rotation) {
+class TvPipMenuTests : TvPipTestBase() {
private val systemUiResources =
packageManager.getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME)
+ private val pipBoundsWhileInMenu: Rect = systemUiResources.run {
+ val bounds = getString(getIdentifier("pip_menu_bounds", "string", SYSTEM_UI_PACKAGE_NAME))
+ Rect.unflattenFromString(bounds) ?: error("Could not retrieve PiP menu bounds")
+ }
private val playButtonDescription = systemUiResources.run {
getString(getIdentifier("pip_play", "string", SYSTEM_UI_PACKAGE_NAME))
}
@@ -52,12 +53,18 @@ class TvPipMenuTests(rotationName: String, rotation: Int)
}
@Test
- fun pipMenu_open() {
+ fun pipMenu_correctPosition() {
val pipMenu = enterPip_openMenu_assertShown()
// Make sure it's fullscreen
assertTrue("Pip menu should be shown fullscreen", pipMenu.isFullscreen(uiDevice))
+ // Make sure the PiP task is positioned where it should be.
+ val activityBounds: Rect = testApp.ui?.visibleBounds
+ ?: error("Could not retrieve PiP Activity bounds")
+ assertTrue("Pip Activity is positioned correctly while Pip menu is shown",
+ pipBoundsWhileInMenu == activityBounds)
+
testApp.closePipWindow()
}
@@ -144,10 +151,4 @@ class TvPipMenuTests(rotationName: String, rotation: Int)
uiDevice.pressWindowKey()
return uiDevice.waitForTvPipMenu() ?: fail("Pip menu should have been shown")
}
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> = rotationParams
- }
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
index 7a9b33b46742..75388bf2a189 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
@@ -32,18 +32,13 @@ import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
/**
* Test Pip Notifications on TV.
* To run this test: `atest WMShellFlickerTests:TvPipNotificationTests`
*/
@RequiresDevice
-@RunWith(Parameterized::class)
-class TvPipNotificationTests(rotationName: String, rotation: Int)
- : TvPipTestBase(rotationName, rotation) {
-
+class TvPipNotificationTests : TvPipTestBase() {
@Before
override fun setUp() {
super.setUp()
@@ -154,10 +149,6 @@ class TvPipNotificationTests(rotationName: String, rotation: Int)
companion object {
private const val TITLE_MEDIA_SESSION_PLAYING = "TestApp media is playing"
private const val TITLE_MEDIA_SESSION_PAUSED = "TestApp media is paused"
-
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> = rotationParams
}
}
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 05f4b0b0d918..d1906ba2e27e 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
@@ -16,40 +16,81 @@
package com.android.wm.shell.flicker.pip.tv
+import android.app.ActivityManager
+import android.app.IActivityManager
+import android.app.IProcessObserver
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
+import android.os.SystemClock
import android.view.Surface.ROTATION_0
import android.view.Surface.rotationToString
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.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.Before
-abstract class TvPipTestBase(rotationName: String, rotation: Int)
- : PipTestBase(rotationName, rotation) {
+abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATION_0) {
private val isTelevision: Boolean
get() = packageManager.run {
hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
}
+ private val systemUiProcessObserver = SystemUiProcessObserver()
@Before
open fun setUp() {
Assume.assumeTrue(isTelevision)
+
+ systemUiProcessObserver.start()
+
uiDevice.wakeUpAndGoToHomeScreen()
}
@After
open fun tearDown() {
+ if (!isTelevision) return
+
testApp.forceStop()
+
+ // Wait for 1 second, and check if the SystemUI has been alive and well since the start.
+ SystemClock.sleep(AFTER_TEXT_PROCESS_CHECK_DELAY)
+ systemUiProcessObserver.stop()
+ assertFalse("SystemUI has died during test execution", systemUiProcessObserver.hasDied)
}
protected fun fail(message: String): Nothing = throw AssertionError(message)
+ inner class SystemUiProcessObserver : IProcessObserver.Stub() {
+ private val activityManager: IActivityManager = ActivityManager.getService()
+ private val uiAutomation = instrumentation.uiAutomation
+ private val systemUiUid = packageManager.getPackageUid(SYSTEM_UI_PACKAGE_NAME, 0)
+ var hasDied: Boolean = false
+
+ fun start() {
+ hasDied = false
+ uiAutomation.adoptShellPermissionIdentity(
+ android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ activityManager.registerProcessObserver(this)
+ }
+
+ fun stop() {
+ activityManager.unregisterProcessObserver(this)
+ uiAutomation.dropShellPermissionIdentity()
+ }
+
+ override fun onForegroundActivitiesChanged(pid: Int, uid: Int, foreground: Boolean) {}
+
+ override fun onForegroundServicesChanged(pid: Int, uid: Int, serviceTypes: Int) {}
+
+ override fun onProcessDied(pid: Int, uid: Int) {
+ if (uid == systemUiUid) hasDied = true
+ }
+ }
+
companion object {
- @JvmStatic
- protected val rotationParams: Collection<Array<Any>> =
- listOf(arrayOf(rotationToString(ROTATION_0), ROTATION_0))
+ private const val AFTER_TEXT_PROCESS_CHECK_DELAY = 1_000L // 1 sec
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
new file mode 100644
index 000000000000..6e10f932c745
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.dsl.runWithFlicker
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS
+import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
+import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test SplitScreen launch.
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class EnterSplitScreenTest(
+ rotationName: String,
+ rotation: Int
+) : SplitScreenTestBase(rotationName, rotation) {
+ private val splitScreenSetup: FlickerBuilder
+ get() = FlickerBuilder(instrumentation).apply {
+ val testLaunchActivity = "launch_splitScreen_test_activity"
+ withTestName {
+ testLaunchActivity
+ }
+ setup {
+ eachRun {
+ uiDevice.wakeUpAndGoToHomeScreen()
+ splitScreenApp.open()
+ uiDevice.pressHome()
+ }
+ }
+ teardown {
+ eachRun {
+ splitScreenApp.forceStop()
+ secondaryApp.forceStop()
+ }
+ }
+ assertions {
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+ }
+ }
+
+ @Test
+ fun testEnterSplitScreen_dockActivity() {
+ val testTag = "testEnterSplitScreen_dockActivity"
+ runWithFlicker(splitScreenSetup) {
+ withTestName { testTag }
+ repeat {
+ TEST_REPETITIONS
+ }
+ transitions {
+ uiDevice.launchSplitScreen()
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsVisible()
+ end("appsEndingBounds", enabled = false) {
+ val entry = this.trace.entries.firstOrNull()
+ ?: throw IllegalStateException("Trace is empty")
+ this.hasVisibleRegion(splitScreenApp.defaultWindowName,
+ splitScreenApp.getPrimaryBounds(
+ entry.getVisibleBounds(DOCKED_STACK_DIVIDER)))
+ }
+ }
+ windowManagerTrace {
+ end {
+ showsAppWindow(splitScreenApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun testEnterSplitScreen_launchToSide() {
+ val testTag = "testEnterSplitScreen_launchToSide"
+ runWithFlicker(splitScreenSetup) {
+ withTestName { testTag }
+ repeat {
+ TEST_REPETITIONS
+ }
+ transitions {
+ secondaryApp.open()
+ uiDevice.pressHome()
+ splitScreenApp.open()
+ uiDevice.pressHome()
+ uiDevice.launchSplitScreen()
+ splitScreenApp.reopenAppFromOverview()
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsVisible()
+ end("appsEndingBounds", enabled = false) {
+ val entry = this.trace.entries.firstOrNull()
+ ?: throw IllegalStateException("Trace is empty")
+ this.hasVisibleRegion(splitScreenApp.defaultWindowName,
+ splitScreenApp.getPrimaryBounds(
+ entry.getVisibleBounds(DOCKED_STACK_DIVIDER)))
+ .and()
+ .hasVisibleRegion(secondaryApp.defaultWindowName,
+ splitScreenApp.getSecondaryBounds(
+ entry.getVisibleBounds(DOCKED_STACK_DIVIDER)))
+ }
+ }
+ windowManagerTrace {
+ end {
+ showsAppWindow(splitScreenApp.defaultWindowName)
+ .and().showsAppWindow(secondaryApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0)
+ return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
new file mode 100644
index 000000000000..17fc862fffec
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.util.Rational
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.dsl.runWithFlicker
+import com.android.server.wm.flicker.helpers.exitSplitScreen
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.resizeSplitScreen
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS
+import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test exit SplitScreen mode.
+ * To run this test: `atest WMShellFlickerTests:ExitSplitScreenTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class ExitSplitScreenTest(
+ rotationName: String,
+ rotation: Int
+) : SplitScreenTestBase(rotationName, rotation) {
+ private val splitScreenSetup: FlickerBuilder
+ get() = FlickerBuilder(instrumentation).apply {
+ val testLaunchActivity = "launch_splitScreen_test_activity"
+ withTestName {
+ testLaunchActivity
+ }
+ setup {
+ eachRun {
+ uiDevice.wakeUpAndGoToHomeScreen()
+ secondaryApp.open()
+ uiDevice.pressHome()
+ splitScreenApp.open()
+ uiDevice.pressHome()
+ uiDevice.launchSplitScreen()
+ }
+ }
+ teardown {
+ eachRun {
+ splitScreenApp.forceStop()!!
+ secondaryApp.forceStop()!!
+ }
+ }
+ }
+
+ @Test
+ fun testEnterSplitScreen_exitPrimarySplitScreenMode() {
+ val testTag = "testEnterSplitScreen_exitPrimarySplitScreenMode"
+ runWithFlicker(splitScreenSetup) {
+ withTestName { testTag }
+ repeat {
+ TEST_REPETITIONS
+ }
+ transitions {
+ uiDevice.exitSplitScreen()
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsInvisible()
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ end {
+ hidesAppWindow(splitScreenApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ @FlakyTest(bugId = 172811376)
+ fun testEnterSplitScreen_exitPrimary_showSecondaryAppFullScreen() {
+ val testTag = "testEnterSplitScreen_exitPrimary_showSecondaryAppFullScreen"
+ runWithFlicker(splitScreenSetup) {
+ withTestName { testTag }
+ repeat {
+ TEST_REPETITIONS
+ }
+ transitions {
+ splitScreenApp.reopenAppFromOverview()
+ uiDevice.resizeSplitScreen(startRatio)
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsInvisible()
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ end {
+ showsAppWindow(splitScreenApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ private val startRatio = Rational(1, 3)
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0)
+ return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt
new file mode 100644
index 000000000000..496fe94ba951
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import com.android.wm.shell.flicker.NonRotationTestBase
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_LABEL
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+
+abstract class SplitScreenTestBase(
+ rotationName: String,
+ rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
+ protected val splitScreenApp = SplitScreenHelper(instrumentation,
+ TEST_APP_SPLITSCREEN_PRIMARY_LABEL,
+ TEST_APP_SPLITSCREEN_PRIMARY_COMPONENT_NAME)
+ protected val secondaryApp = SplitScreenHelper(instrumentation,
+ TEST_APP_SPLITSCREEN_SECONDARY_LABEL,
+ TEST_APP_SPLITSCREEN_SECONDARY_COMPONENT_NAME)
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/Android.bp b/libs/WindowManager/Shell/tests/flicker/test-apps/Android.bp
deleted file mode 100644
index e69de29bb2d1..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/Android.bp
+++ /dev/null
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 7f8321f3fa3d..2ce120448ac4 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -37,6 +37,7 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
+
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity"
android:label="ImeApp"
@@ -50,5 +51,27 @@
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
+
+ <activity android:name=".SplitScreenActivity"
+ android:resizeableActivity="true"
+ android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenActivity"
+ android:label="SplitScreenPrimaryApp"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".SplitScreenSecondaryActivity"
+ android:resizeableActivity="true"
+ android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenSecondaryActivity"
+ android:label="SplitScreenSecondaryApp"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
index 0e79d03cc3ee..b4a4c165cc7b 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -25,7 +25,48 @@
android:id="@+id/enter_pip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="Enter PIP"/>
+ android:text="Enter PIP"
+ android:onClick="enterPip"/>
+
+ <RadioGroup
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:checkedButton="@id/ratio_default">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Ratio"/>
+
+ <RadioButton
+ android:id="@+id/ratio_default"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Default"
+ android:onClick="onRatioSelected"/>
+
+ <RadioButton
+ android:id="@+id/ratio_square"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Square [1:1]"
+ android:onClick="onRatioSelected"/>
+
+ <RadioButton
+ android:id="@+id/ratio_wide"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Wide [2:1]"
+ android:onClick="onRatioSelected"/>
+
+ <RadioButton
+ android:id="@+id/ratio_tall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Tall [1:2]"
+ android:onClick="onRatioSelected"/>
+ </RadioGroup>
<TextView
android:layout_width="wrap_content"
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml
new file mode 100644
index 000000000000..84789f5a6c02
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/holo_green_light">
+
+ <TextView
+ android:id="@+id/SplitScreenTest"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_vertical|center_horizontal"
+ android:text="PrimaryActivity"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
new file mode 100644
index 000000000000..674bb70ad01e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/holo_blue_light">
+
+ <TextView
+ android:id="@+id/SplitScreenTest"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_vertical|center_horizontal"
+ android:text="SecondaryActivity"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
index b60068a60b34..d2fcd0d31558 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
@@ -26,12 +26,12 @@ import static android.media.session.PlaybackState.STATE_STOPPED;
import android.app.Activity;
import android.app.PictureInPictureParams;
-import android.graphics.Rect;
import android.media.MediaMetadata;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.util.Rational;
+import android.view.View;
import android.view.Window;
import android.view.WindowManager;
@@ -47,6 +47,12 @@ public class PipActivity extends Activity {
*/
private static final String TITLE_STATE_PAUSED = "TestApp media is paused";
+ private static final Rational RATIO_DEFAULT = null;
+ private static final Rational RATIO_SQUARE = new Rational(1, 1);
+ private static final Rational RATIO_WIDE = new Rational(2, 1);
+ private static final Rational RATIO_TALL = new Rational(1, 2);
+
+ private PictureInPictureParams.Builder mPipParamsBuilder;
private MediaSession mMediaSession;
private final PlaybackState.Builder mPlaybackStateBuilder = new PlaybackState.Builder()
.setActions(ACTION_PLAY | ACTION_PAUSE | ACTION_STOP)
@@ -66,11 +72,8 @@ public class PipActivity extends Activity {
setContentView(R.layout.activity_pip);
- final PictureInPictureParams pipParams = new PictureInPictureParams.Builder()
- .setAspectRatio(new Rational(1, 1))
- .setSourceRectHint(new Rect(0, 0, 100, 100))
- .build();
- findViewById(R.id.enter_pip).setOnClickListener(v -> enterPictureInPictureMode(pipParams));
+ mPipParamsBuilder = new PictureInPictureParams.Builder()
+ .setAspectRatio(RATIO_DEFAULT);
findViewById(R.id.media_session_start)
.setOnClickListener(v -> updateMediaSessionState(STATE_PLAYING));
@@ -97,6 +100,30 @@ public class PipActivity extends Activity {
});
}
+ public void enterPip(View v) {
+ enterPictureInPictureMode(mPipParamsBuilder.build());
+ }
+
+ public void onRatioSelected(View v) {
+ switch (v.getId()) {
+ case R.id.ratio_default:
+ mPipParamsBuilder.setAspectRatio(RATIO_DEFAULT);
+ break;
+
+ case R.id.ratio_square:
+ mPipParamsBuilder.setAspectRatio(RATIO_SQUARE);
+ break;
+
+ case R.id.ratio_wide:
+ mPipParamsBuilder.setAspectRatio(RATIO_WIDE);
+ break;
+
+ case R.id.ratio_tall:
+ mPipParamsBuilder.setAspectRatio(RATIO_TALL);
+ break;
+ }
+ }
+
private void updateMediaSessionState(int newState) {
if (mPlaybackState.getState() == newState) {
return;
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenActivity.java
new file mode 100644
index 000000000000..9c82eea1e8b8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SplitScreenActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_splitscreen);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java
new file mode 100644
index 000000000000..baa1e6fdd1e9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SplitScreenSecondaryActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_splitscreen_secondary);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/LetterboxTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/LetterboxTaskListenerTest.java
deleted file mode 100644
index 45d4d5d347dd..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/LetterboxTaskListenerTest.java
+++ /dev/null
@@ -1,125 +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;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-
-import static org.mockito.ArgumentMatchers.eq;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.view.SurfaceControl;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link LetterboxTaskListener}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class LetterboxTaskListenerTest {
-
- private static final Rect ACTIVITY_BOUNDS = new Rect(300, 200, 700, 400);
- private static final Rect TASK_BOUNDS = new Rect(200, 100, 800, 500);
- private static final Rect TASK_BOUNDS_2 = new Rect(300, 200, 800, 500);
- private static final Point TASK_POSITION_IN_PARENT = new Point(100, 50);
- private static final Point TASK_POSITION_IN_PARENT_2 = new Point(200, 100);
-
- private static final Rect EXPECTED_WINDOW_CROP = new Rect(100, 100, 500, 300);
- private static final Rect EXPECTED_WINDOW_CROP_2 = new Rect(0, 0, 400, 200);
-
- private static final RunningTaskInfo TASK_INFO = createTaskInfo(
- /* taskId */ 1, ACTIVITY_BOUNDS, TASK_BOUNDS, TASK_POSITION_IN_PARENT);
-
- private static final RunningTaskInfo TASK_INFO_2 = createTaskInfo(
- /* taskId */ 1, ACTIVITY_BOUNDS, TASK_BOUNDS_2, TASK_POSITION_IN_PARENT_2);
-
- @Mock private SurfaceControl mLeash;
- @Mock private SurfaceControl.Transaction mTransaction;
- private LetterboxTaskListener mLetterboxTaskListener;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mLetterboxTaskListener = new LetterboxTaskListener(
- new SyncTransactionQueue(
- new TransactionPool() {
- @Override
- public SurfaceControl.Transaction acquire() {
- return mTransaction;
- }
-
- @Override
- public void release(SurfaceControl.Transaction t) {
- }
- },
- new Handler(Looper.getMainLooper())));
- }
-
- @Test
- public void testOnTaskAppearedAndonTaskInfoChanged_setCorrectPositionAndCrop() {
- mLetterboxTaskListener.onTaskAppeared(TASK_INFO, mLeash);
-
- verify(mTransaction).setPosition(
- eq(mLeash),
- eq((float) TASK_POSITION_IN_PARENT.x),
- eq((float) TASK_POSITION_IN_PARENT.y));
- // Should return activty coordinates offset by task coordinates
- verify(mTransaction).setWindowCrop(eq(mLeash), eq(EXPECTED_WINDOW_CROP));
-
- mLetterboxTaskListener.onTaskInfoChanged(TASK_INFO_2);
-
- verify(mTransaction).setPosition(
- eq(mLeash),
- eq((float) TASK_POSITION_IN_PARENT_2.x),
- eq((float) TASK_POSITION_IN_PARENT_2.y));
- // Should return activty coordinates offset by task coordinates
- verify(mTransaction).setWindowCrop(eq(mLeash), eq(EXPECTED_WINDOW_CROP_2));
- }
-
- @Test(expected = RuntimeException.class)
- public void testOnTaskAppeared_calledSecondTimeWithSameTaskId_throwsException() {
- mLetterboxTaskListener.onTaskAppeared(TASK_INFO, mLeash);
- mLetterboxTaskListener.onTaskAppeared(TASK_INFO, mLeash);
- }
-
- private static RunningTaskInfo createTaskInfo(
- int taskId,
- final Rect activityBounds,
- final Rect taskBounds,
- final Point taskPositionInParent) {
- RunningTaskInfo taskInfo = new RunningTaskInfo();
- taskInfo.taskId = taskId;
- taskInfo.configuration.windowConfiguration.setBounds(taskBounds);
- taskInfo.letterboxActivityBounds = Rect.copyOrNull(activityBounds);
- taskInfo.positionInParent = new Point(taskPositionInParent);
- return taskInfo;
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
new file mode 100644
index 000000000000..754f73246c86
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
@@ -0,0 +1,102 @@
+/*
+ * 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.apppairs;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.hardware.display.DisplayManager;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TaskStackListenerImpl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link AppPair} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppPairTests extends ShellTestCase {
+
+ private AppPairsController mController;
+ @Mock private SyncTransactionQueue mSyncQueue;
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+ @Mock private DisplayController mDisplayController;
+ @Mock private TaskStackListenerImpl mTaskStackListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new TestAppPairsController(
+ mTaskOrganizer,
+ mSyncQueue,
+ mDisplayController,
+ mTaskStackListener);
+ when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
+ when(mDisplayController.getDisplay(anyInt())).thenReturn(
+ mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
+ }
+
+ @After
+ public void tearDown() {}
+
+ @Test
+ @UiThreadTest
+ public void testContains() {
+ final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
+ final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
+
+ final AppPair pair = mController.pairInner(task1, task2);
+ assertThat(pair.contains(task1.taskId)).isTrue();
+ assertThat(pair.contains(task2.taskId)).isTrue();
+
+ pair.unpair();
+ assertThat(pair.contains(task1.taskId)).isFalse();
+ assertThat(pair.contains(task2.taskId)).isFalse();
+ }
+
+ @Test
+ @UiThreadTest
+ public void testVanishUnpairs() {
+ final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
+ final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
+
+ final AppPair pair = mController.pairInner(task1, task2);
+ assertThat(pair.contains(task1.taskId)).isTrue();
+ assertThat(pair.contains(task2.taskId)).isTrue();
+
+ pair.onTaskVanished(task1);
+ assertThat(pair.contains(task1.taskId)).isFalse();
+ assertThat(pair.contains(task2.taskId)).isFalse();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
new file mode 100644
index 000000000000..6d441ab898ec
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
@@ -0,0 +1,106 @@
+/*
+ * 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.apppairs;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.hardware.display.DisplayManager;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TaskStackListenerImpl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link AppPairsController} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppPairsControllerTests extends ShellTestCase {
+ private TestAppPairsController mController;
+ private TestAppPairsPool mPool;
+ @Mock private SyncTransactionQueue mSyncQueue;
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+ @Mock private DisplayController mDisplayController;
+ @Mock private TaskStackListenerImpl mTaskStackListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new TestAppPairsController(
+ mTaskOrganizer,
+ mSyncQueue,
+ mDisplayController,
+ mTaskStackListener);
+ mPool = mController.getPool();
+ when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
+ when(mDisplayController.getDisplay(anyInt())).thenReturn(
+ mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
+ }
+
+ @After
+ public void tearDown() {}
+
+ @Test
+ @UiThreadTest
+ public void testPairUnpair() {
+ final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
+ final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
+
+ final AppPair pair = mController.pairInner(task1, task2);
+ assertThat(pair.contains(task1.taskId)).isTrue();
+ assertThat(pair.contains(task2.taskId)).isTrue();
+ assertThat(mPool.poolSize()).isGreaterThan(0);
+
+ mController.unpair(task2.taskId);
+ assertThat(pair.contains(task1.taskId)).isFalse();
+ assertThat(pair.contains(task2.taskId)).isFalse();
+ assertThat(mPool.poolSize()).isGreaterThan(1);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testUnpair_DontReleaseToPool() {
+ final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
+ final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
+
+ final AppPair pair = mController.pairInner(task1, task2);
+ assertThat(pair.contains(task1.taskId)).isTrue();
+ assertThat(pair.contains(task2.taskId)).isTrue();
+
+ mController.unpair(task2.taskId, false /* releaseToPool */);
+ assertThat(pair.contains(task1.taskId)).isFalse();
+ assertThat(pair.contains(task2.taskId)).isFalse();
+ assertThat(mPool.poolSize()).isEqualTo(1);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
new file mode 100644
index 000000000000..d3dbbfe37985
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
@@ -0,0 +1,75 @@
+/*
+ * 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.apppairs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TaskStackListenerImpl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link AppPairsPool} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppPairsPoolTests {
+ private TestAppPairsController mController;
+ private TestAppPairsPool mPool;
+ @Mock private SyncTransactionQueue mSyncQueue;
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+ @Mock private DisplayController mDisplayController;
+ @Mock private TaskStackListenerImpl mTaskStackListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new TestAppPairsController(
+ mTaskOrganizer,
+ mSyncQueue,
+ mDisplayController,
+ mTaskStackListener);
+ mPool = mController.getPool();
+ }
+
+ @After
+ public void tearDown() {}
+
+ @Test
+ public void testInitialState() {
+ // Pool should always start off with at least 1 entry.
+ assertThat(mPool.poolSize()).isGreaterThan(0);
+ }
+
+ @Test
+ public void testAcquireRelease() {
+ assertThat(mPool.poolSize()).isGreaterThan(0);
+ final AppPair appPair = mPool.acquire();
+ assertThat(mPool.poolSize()).isGreaterThan(0);
+ mPool.release(appPair);
+ assertThat(mPool.poolSize()).isGreaterThan(1);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
new file mode 100644
index 000000000000..e61cc91c394b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
@@ -0,0 +1,37 @@
+/*
+ * 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.apppairs;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TaskStackListenerImpl;
+
+public class TestAppPairsController extends AppPairsController {
+ TestAppPairsPool mPool;
+
+ public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
+ DisplayController displayController, TaskStackListenerImpl taskStackListener) {
+ super(organizer, syncQueue, displayController, taskStackListener);
+ mPool = new TestAppPairsPool(this);
+ setPairsPool(mPool);
+ }
+
+ TestAppPairsPool getPool() {
+ return mPool;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java
new file mode 100644
index 000000000000..080f2075344a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java
@@ -0,0 +1,34 @@
+/*
+ * 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.apppairs;
+
+import android.app.ActivityManager;
+
+public class TestAppPairsPool extends AppPairsPool{
+ TestAppPairsPool(AppPairsController controller) {
+ super(controller);
+ }
+
+ @Override
+ void incrementPool() {
+ final AppPair entry = new AppPair(mController);
+ final ActivityManager.RunningTaskInfo info =
+ new TestRunningTaskInfoBuilder().build();
+ entry.onTaskAppeared(info, null /* leash */);
+ release(entry);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestRunningTaskInfoBuilder.java
new file mode 100644
index 000000000000..76d3a6aaee22
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestRunningTaskInfoBuilder.java
@@ -0,0 +1,43 @@
+/*
+ * 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.apppairs;
+
+import android.app.ActivityManager;
+import android.graphics.Rect;
+import android.window.IWindowContainerToken;
+import android.window.WindowContainerToken;
+
+public class TestRunningTaskInfoBuilder {
+ static int sNextTaskId = 500;
+ private Rect mBounds = new Rect(0, 0, 100, 100);
+ private WindowContainerToken mToken =
+ new WindowContainerToken(new IWindowContainerToken.Default());
+
+ TestRunningTaskInfoBuilder setBounds(Rect bounds) {
+ mBounds.set(bounds);
+ return this;
+ }
+
+ ActivityManager.RunningTaskInfo build() {
+ final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
+ info.taskId = sNextTaskId++;
+ info.configuration.windowConfiguration.setBounds(mBounds);
+ info.token = mToken;
+ info.isResizeable = true;
+ return info;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxConfigControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxConfigControllerTest.java
new file mode 100644
index 000000000000..29233366d4f3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxConfigControllerTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.letterbox;
+
+import static org.junit.Assert.assertEquals;
+
+import android.view.Gravity;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link LetterboxConfigController}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class LetterboxConfigControllerTest extends ShellTestCase {
+
+ private LetterboxConfigController mLetterboxConfigController;
+
+ @Before
+ public void setUp() {
+ mLetterboxConfigController = new LetterboxConfigController(getContext());
+ }
+
+ @Test
+ public void testGetPortraitGravity_noOverrides_returnConfigValue() {
+ assertEquals(
+ mLetterboxConfigController.getPortraitGravity(),
+ getContext().getResources().getInteger(R.integer.config_letterboxPortraitGravity));
+ }
+
+ @Test
+ public void testGetLandscapeGravity_noOverrides_returnConfigValue() {
+ assertEquals(
+ mLetterboxConfigController.getLandscapeGravity(),
+ getContext().getResources().getInteger(R.integer.config_letterboxLandscapeGravity));
+ }
+
+ @Test
+ public void testSetPortraitGravity_validValue_savesValue() {
+ mLetterboxConfigController.setPortraitGravity(Gravity.BOTTOM);
+ assertEquals(mLetterboxConfigController.getPortraitGravity(), Gravity.BOTTOM);
+
+ mLetterboxConfigController.setPortraitGravity(Gravity.CENTER);
+ assertEquals(mLetterboxConfigController.getPortraitGravity(), Gravity.CENTER);
+
+ mLetterboxConfigController.setPortraitGravity(Gravity.TOP);
+ assertEquals(mLetterboxConfigController.getPortraitGravity(), Gravity.TOP);
+ }
+
+ @Test
+ public void testSetLandscapeGravity_validValue_savesValue() {
+ mLetterboxConfigController.setLandscapeGravity(Gravity.LEFT);
+ assertEquals(mLetterboxConfigController.getLandscapeGravity(), Gravity.LEFT);
+
+ mLetterboxConfigController.setLandscapeGravity(Gravity.CENTER);
+ assertEquals(mLetterboxConfigController.getLandscapeGravity(), Gravity.CENTER);
+
+ mLetterboxConfigController.setLandscapeGravity(Gravity.RIGHT);
+ assertEquals(mLetterboxConfigController.getLandscapeGravity(), Gravity.RIGHT);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetPortraitGravity_invalidValue_throwsException() {
+ mLetterboxConfigController.setPortraitGravity(Gravity.RIGHT);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSetLandscapeGravity_invalidValue_throwsException() {
+ mLetterboxConfigController.setLandscapeGravity(Gravity.TOP);
+ }
+
+ @Test
+ public void testResetPortraitGravity() {
+ int defaultGravity =
+ getContext().getResources().getInteger(R.integer.config_letterboxPortraitGravity);
+
+ mLetterboxConfigController.setPortraitGravity(Gravity.BOTTOM);
+ mLetterboxConfigController.resetPortraitGravity();
+ assertEquals(mLetterboxConfigController.getPortraitGravity(), defaultGravity);
+
+ mLetterboxConfigController.setPortraitGravity(Gravity.CENTER);
+ mLetterboxConfigController.resetPortraitGravity();
+ assertEquals(mLetterboxConfigController.getPortraitGravity(), defaultGravity);
+
+ mLetterboxConfigController.setPortraitGravity(Gravity.TOP);
+ mLetterboxConfigController.resetPortraitGravity();
+ assertEquals(mLetterboxConfigController.getPortraitGravity(), defaultGravity);
+ }
+
+ @Test
+ public void testResetLandscapeGravity() {
+ int defaultGravity =
+ getContext().getResources().getInteger(R.integer.config_letterboxLandscapeGravity);
+
+ mLetterboxConfigController.setLandscapeGravity(Gravity.RIGHT);
+ mLetterboxConfigController.resetLandscapeGravity();
+ assertEquals(mLetterboxConfigController.getLandscapeGravity(), defaultGravity);
+
+ mLetterboxConfigController.setLandscapeGravity(Gravity.CENTER);
+ mLetterboxConfigController.resetLandscapeGravity();
+ assertEquals(mLetterboxConfigController.getLandscapeGravity(), defaultGravity);
+
+ mLetterboxConfigController.setLandscapeGravity(Gravity.LEFT);
+ mLetterboxConfigController.resetLandscapeGravity();
+ assertEquals(mLetterboxConfigController.getLandscapeGravity(), defaultGravity);
+ }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java
new file mode 100644
index 000000000000..0f719afd08b3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java
@@ -0,0 +1,320 @@
+/*
+ * 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.letterbox;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Gravity;
+import android.view.SurfaceControl;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TransactionPool;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link LetterboxTaskListener}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class LetterboxTaskListenerTest extends ShellTestCase {
+
+ @Mock private SurfaceControl mLeash;
+ @Mock private SurfaceControl.Transaction mTransaction;
+ @Mock private WindowManager mWindowManager;
+ @Mock private WindowMetrics mWindowMetrics;
+ @Mock private WindowInsets mWindowInsets;
+ private LetterboxTaskListener mLetterboxTaskListener;
+ private LetterboxConfigController mLetterboxConfigController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mLetterboxConfigController = new LetterboxConfigController(getContext());
+ mLetterboxTaskListener = new LetterboxTaskListener(
+ new SyncTransactionQueue(
+ new TransactionPool() {
+ @Override
+ public SurfaceControl.Transaction acquire() {
+ return mTransaction;
+ }
+
+ @Override
+ public void release(SurfaceControl.Transaction t) {
+ }
+ },
+ new Handler(Looper.getMainLooper())),
+ mLetterboxConfigController,
+ mWindowManager);
+
+ when(mWindowManager.getMaximumWindowMetrics()).thenReturn(mWindowMetrics);
+ when(mWindowMetrics.getWindowInsets()).thenReturn(mWindowInsets);
+ }
+
+ @Test
+ public void testOnTaskInfoChanged_updatesPositionAndCrop() {
+ setWindowBoundsAndInsets(
+ /* windowBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
+ Insets.NONE);
+
+ mLetterboxConfigController.setLandscapeGravity(Gravity.CENTER);
+ mLetterboxTaskListener.onTaskAppeared(
+ createTaskInfo(
+ /* taskId */ 1,
+ /* parentBounds */ new Rect(0, 0, 200, 100),
+ /* activityBounds */ new Rect(75, 0, 125, 75),
+ /* taskBounds */ new Rect(50, 0, 125, 100)),
+ mLeash);
+
+ // Task doesn't need to repositioned
+ verifySetPosition(50, 0);
+ // Should return activity coordinates offset by task coordinates
+ verifySetWindowCrop(new Rect(25, 0, 75, 75));
+
+ mLetterboxTaskListener.onTaskInfoChanged(
+ createTaskInfo(
+ /* taskId */ 1,
+ /* parentBounds */ new Rect(0, 0, 200, 100),
+ // Activity is offset by 25 to the left
+ /* activityBounds */ new Rect(50, 0, 100, 75),
+ /* taskBounds */ new Rect(50, 0, 125, 100)));
+
+ // Task needs to be repositioned by 25 to the left
+ verifySetPosition(75, 0);
+ // Should return activity coordinates offset by task coordinates
+ verifySetWindowCrop(new Rect(0, 0, 50, 75));
+ }
+
+ @Test
+ public void testOnTaskInfoAppeared_landscapeWithLeftGravity() {
+ mLetterboxConfigController.setLandscapeGravity(Gravity.LEFT);
+ setWindowBoundsAndInsets(
+ /* windowBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
+ Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 10));
+
+ mLetterboxTaskListener.onTaskAppeared(
+ createTaskInfo(
+ /* taskId */ 1,
+ /* parentBounds */ new Rect(0, 0, 200, 100),
+ /* activityBounds */ new Rect(150, 0, 200, 75),
+ /* taskBounds */ new Rect(125, 0, 200, 100)),
+ mLeash);
+
+ verifySetPosition(-15, 0);
+ // Should return activity coordinates offset by task coordinates minus unwanted right inset
+ verifySetWindowCrop(new Rect(25, 0, 65, 75));
+ }
+
+ @Test
+ public void testOnTaskInfoAppeared_landscapeWithCenterGravity() {
+ mLetterboxConfigController.setLandscapeGravity(Gravity.CENTER);
+ setWindowBoundsAndInsets(
+ /* windowBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
+ Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 10));
+
+ mLetterboxTaskListener.onTaskAppeared(
+ createTaskInfo(
+ /* taskId */ 1,
+ /* parentBounds */ new Rect(0, 0, 200, 100),
+ /* activityBounds */ new Rect(150, 0, 200, 75),
+ /* taskBounds */ new Rect(125, 0, 200, 100)),
+ mLeash);
+
+ verifySetPosition(55, 0);
+ // Should return activity coordinates offset by task coordinates minus unwanted right inset
+ verifySetWindowCrop(new Rect(25, 0, 65, 75));
+ }
+
+ @Test
+ public void testOnTaskInfoAppeared_landscapeWithRightGravity() {
+ mLetterboxConfigController.setLandscapeGravity(Gravity.RIGHT);
+ setWindowBoundsAndInsets(
+ /* windowBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
+ Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 10));
+
+ mLetterboxTaskListener.onTaskAppeared(
+ createTaskInfo(
+ /* taskId */ 1,
+ /* parentBounds */ new Rect(0, 0, 200, 100),
+ /* activityBounds */ new Rect(50, 0, 100, 75),
+ /* taskBounds */ new Rect(25, 0, 100, 100)),
+ mLeash);
+
+ verifySetPosition(115, 0);
+ // Should return activity coordinates offset by task coordinates
+ verifySetWindowCrop(new Rect(25, 0, 75, 75));
+ }
+
+ @Test
+ public void testOnTaskInfoAppeared_portraitWithTopGravity() {
+ mLetterboxConfigController.setPortraitGravity(Gravity.TOP);
+ setWindowBoundsAndInsets(
+ /* windowBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
+ Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 20));
+
+ mLetterboxTaskListener.onTaskAppeared(
+ createTaskInfo(
+ /* taskId */ 1,
+ /* parentBounds */ new Rect(0, 0, 100, 150),
+ /* activityBounds */ new Rect(0, 75, 50, 125),
+ /* taskBounds */ new Rect(0, 50, 100, 125)),
+ mLeash);
+
+ verifySetPosition(20, -15);
+ // Should return activity coordinates offset by task coordinates minus unwanted left inset
+ verifySetWindowCrop(new Rect(10, 25, 50, 75));
+ }
+
+ @Test
+ public void testOnTaskInfoAppeared_portraitWithCenterGravity() {
+ mLetterboxConfigController.setPortraitGravity(Gravity.CENTER);
+ setWindowBoundsAndInsets(
+ /* windowBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
+ Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 20));
+
+ mLetterboxTaskListener.onTaskAppeared(
+ createTaskInfo(
+ /* taskId */ 1,
+ /* parentBounds */ new Rect(0, 0, 100, 150),
+ /* activityBounds */ new Rect(0, 75, 50, 125),
+ /* taskBounds */ new Rect(0, 50, 100, 125)),
+ mLeash);
+
+ verifySetPosition(20, 20);
+ // Should return activity coordinates offset by task coordinates minus unwanted left inset
+ verifySetWindowCrop(new Rect(10, 25, 50, 75));
+ }
+
+ @Test
+ public void testOnTaskInfoAppeared_portraitWithBottomGravity() {
+ mLetterboxConfigController.setPortraitGravity(Gravity.BOTTOM);
+ setWindowBoundsAndInsets(
+ /* windowBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
+ Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 20));
+
+ mLetterboxTaskListener.onTaskAppeared(
+ createTaskInfo(
+ /* taskId */ 1,
+ /* parentBounds */ new Rect(0, 0, 100, 150),
+ /* activityBounds */ new Rect(0, 75, 50, 125),
+ /* taskBounds */ new Rect(0, 50, 100, 125)),
+ mLeash);
+
+ verifySetPosition(20, 55);
+ // Should return activity coordinates offset by task coordinates minus unwanted left inset
+ verifySetWindowCrop(new Rect(10, 25, 50, 75));
+ }
+
+ @Test
+ public void testOnTaskInfoAppeared_partlyOverlapsWithAllInsets() {
+ mLetterboxConfigController.setPortraitGravity(Gravity.TOP);
+ setWindowBoundsAndInsets(
+ /* windowBounds= */ new Rect(0, 0, 200, 125), // equal to parent bounds
+ Insets.of(/* left= */ 25, /* top= */ 25, /* right= */ 35, /* bottom= */ 15));
+
+ mLetterboxTaskListener.onTaskAppeared(
+ createTaskInfo(
+ /* taskId */ 1,
+ /* parentBounds */ new Rect(0, 0, 200, 125),
+ /* activityBounds */ new Rect(15, 0, 175, 120),
+ /* taskBounds */ new Rect(0, 0, 100, 125)), // equal to parent bounds
+ mLeash);
+
+ // Activity fully covers parent bounds with insets so doesn't need to be moved.
+ verifySetPosition(0, 0);
+ // Should return activity coordinates offset by task coordinates minus all insets
+ // except top one (keep status bar decor visible).
+ verifySetWindowCrop(new Rect(25, 0, 165, 110));
+ }
+
+ @Test
+ public void testOnTaskInfoAppeared_parentShiftedLikeInOneHandedMode() {
+ mLetterboxConfigController.setPortraitGravity(Gravity.TOP);
+ setWindowBoundsAndInsets(
+ /* windowBounds= */ new Rect(0, 0, 100, 150),
+ Insets.of(/* left= */ 0, /* top= */ 10, /* right= */ 0, /* bottom= */ 0));
+
+ mLetterboxTaskListener.onTaskAppeared(
+ createTaskInfo(
+ /* taskId */ 1,
+ /* parentBounds */ new Rect(0, 75, 100, 225),
+ /* activityBounds */ new Rect(25, 75, 75, 125),
+ /* taskBounds */ new Rect(0, 75, 100, 125)),
+ mLeash);
+
+ verifySetPosition(0, 0);
+ verifySetWindowCrop(new Rect(25, 0, 75, 50));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testOnTaskAppeared_calledSecondTimeWithSameTaskId_throwsException() {
+ setWindowBoundsAndInsets(new Rect(), Insets.NONE);
+ RunningTaskInfo taskInfo =
+ createTaskInfo(/* taskId */ 1, new Rect(), new Rect(), new Rect());
+ mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash);
+ mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash);
+ }
+
+ private void setWindowBoundsAndInsets(Rect windowBounds, Insets insets) {
+ when(mWindowMetrics.getBounds()).thenReturn(windowBounds);
+ when(mWindowInsets.getInsets(anyInt())).thenReturn(insets);
+ }
+
+ private void verifySetPosition(int x, int y) {
+ verify(mTransaction).setPosition(eq(mLeash), eq((float) x), eq((float) y));
+ }
+
+ private void verifySetWindowCrop(final Rect crop) {
+ // Should return activty coordinates offset by task coordinates
+ verify(mTransaction).setWindowCrop(eq(mLeash), eq(crop));
+ }
+
+ private static RunningTaskInfo createTaskInfo(
+ int taskId,
+ final Rect parentBounds,
+ final Rect activityBounds,
+ final Rect taskBounds) {
+ RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.parentBounds = parentBounds;
+ taskInfo.configuration.windowConfiguration.setBounds(taskBounds);
+ taskInfo.letterboxActivityBounds = Rect.copyOrNull(activityBounds);
+
+ return taskInfo;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index ba60d3d03f99..a65d832359d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -36,7 +36,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
/**
- * Unit tests against {@link PipBoundsHandler}, including but not limited to:
+ * Unit tests against {@link PipBoundsAlgorithm}, including but not limited to:
* - default/movement bounds
* - save/restore PiP position on application lifecycle
* - save/restore PiP position on screen rotation
@@ -44,14 +44,15 @@ import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class PipBoundsHandlerTest extends ShellTestCase {
+public class PipBoundsAlgorithmTest extends ShellTestCase {
private static final int ROUNDING_ERROR_MARGIN = 16;
private static final float ASPECT_RATIO_ERROR_MARGIN = 0.01f;
private static final float DEFAULT_ASPECT_RATIO = 1f;
private static final float MIN_ASPECT_RATIO = 0.5f;
private static final float MAX_ASPECT_RATIO = 2f;
+ private static final int DEFAULT_MIN_EDGE_SIZE = 100;
- private PipBoundsHandler mPipBoundsHandler;
+ private PipBoundsAlgorithm mPipBoundsAlgorithm;
private DisplayInfo mDefaultDisplayInfo;
private PipBoundsState mPipBoundsState;
@@ -59,7 +60,7 @@ public class PipBoundsHandlerTest extends ShellTestCase {
public void setUp() throws Exception {
initializeMockResources();
mPipBoundsState = new PipBoundsState(mContext);
- mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState);
+ mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState);
mPipBoundsState.setDisplayInfo(mDefaultDisplayInfo);
}
@@ -73,7 +74,8 @@ public class PipBoundsHandlerTest extends ShellTestCase {
com.android.internal.R.integer.config_defaultPictureInPictureGravity,
Gravity.END | Gravity.BOTTOM);
res.addOverride(
- com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, 100);
+ com.android.internal.R.dimen.default_minimal_size_pip_resizable_task,
+ DEFAULT_MIN_EDGE_SIZE);
res.addOverride(
com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets,
"16x16");
@@ -93,7 +95,7 @@ public class PipBoundsHandlerTest extends ShellTestCase {
@Test
public void getDefaultAspectRatio() {
assertEquals("Default aspect ratio matches resources",
- DEFAULT_ASPECT_RATIO, mPipBoundsHandler.getDefaultAspectRatio(),
+ DEFAULT_ASPECT_RATIO, mPipBoundsAlgorithm.getDefaultAspectRatio(),
ASPECT_RATIO_ERROR_MARGIN);
}
@@ -104,14 +106,135 @@ public class PipBoundsHandlerTest extends ShellTestCase {
res.addOverride(com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio,
newDefaultAspectRatio);
- mPipBoundsHandler.onConfigurationChanged(mContext);
+ mPipBoundsAlgorithm.onConfigurationChanged(mContext);
assertEquals("Default aspect ratio should be reloaded",
- mPipBoundsHandler.getDefaultAspectRatio(), newDefaultAspectRatio,
+ mPipBoundsAlgorithm.getDefaultAspectRatio(), newDefaultAspectRatio,
ASPECT_RATIO_ERROR_MARGIN);
}
@Test
+ public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() {
+ final Size defaultSize = mPipBoundsAlgorithm.getSizeForAspectRatio(DEFAULT_ASPECT_RATIO,
+ DEFAULT_MIN_EDGE_SIZE, mDefaultDisplayInfo.logicalWidth,
+ mDefaultDisplayInfo.logicalHeight);
+
+ mPipBoundsState.setOverrideMinSize(null);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ assertEquals(defaultSize, new Size(defaultBounds.width(), defaultBounds.height()));
+ assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds),
+ ASPECT_RATIO_ERROR_MARGIN);
+ }
+
+ @Test
+ public void getDefaultBounds_widerOverrideMinSize_matchesMinSizeWidthAndDefaultAspectRatio() {
+ overrideDefaultAspectRatio(1.0f);
+ // The min size's aspect ratio is greater than the default aspect ratio.
+ final Size overrideMinSize = new Size(150, 120);
+
+ mPipBoundsState.setOverrideMinSize(overrideMinSize);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ // The default aspect ratio should trump the min size aspect ratio.
+ assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds),
+ ASPECT_RATIO_ERROR_MARGIN);
+ // The width of the min size is still used with the default aspect ratio.
+ assertEquals(overrideMinSize.getWidth(), defaultBounds.width());
+ }
+
+ @Test
+ public void getDefaultBounds_tallerOverrideMinSize_matchesMinSizeHeightAndDefaultAspectRatio() {
+ overrideDefaultAspectRatio(1.0f);
+ // The min size's aspect ratio is greater than the default aspect ratio.
+ final Size overrideMinSize = new Size(120, 150);
+
+ mPipBoundsState.setOverrideMinSize(overrideMinSize);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ // The default aspect ratio should trump the min size aspect ratio.
+ assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds),
+ ASPECT_RATIO_ERROR_MARGIN);
+ // The height of the min size is still used with the default aspect ratio.
+ assertEquals(overrideMinSize.getHeight(), defaultBounds.height());
+ }
+
+ @Test
+ public void getDefaultBounds_imeShowing_offsetByImeHeight() {
+ final int imeHeight = 30;
+ mPipBoundsState.setImeVisibility(false, 0);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ mPipBoundsState.setImeVisibility(true, imeHeight);
+ final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds();
+
+ assertEquals(imeHeight, defaultBounds.top - defaultBoundsWithIme.top);
+ }
+
+ @Test
+ public void getDefaultBounds_shelfShowing_offsetByShelfHeight() {
+ final int shelfHeight = 30;
+ mPipBoundsState.setShelfVisibility(false, 0);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ mPipBoundsState.setShelfVisibility(true, shelfHeight);
+ final Rect defaultBoundsWithShelf = mPipBoundsAlgorithm.getDefaultBounds();
+
+ assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithShelf.top);
+ }
+
+ @Test
+ public void getDefaultBounds_imeAndShelfShowing_offsetByTallest() {
+ final int imeHeight = 30;
+ final int shelfHeight = 40;
+ mPipBoundsState.setImeVisibility(false, 0);
+ mPipBoundsState.setShelfVisibility(false, 0);
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ mPipBoundsState.setImeVisibility(true, imeHeight);
+ mPipBoundsState.setShelfVisibility(true, shelfHeight);
+ final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds();
+
+ assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithIme.top);
+ }
+
+ @Test
+ public void getDefaultBounds_boundsAtDefaultGravity() {
+ final Rect insetBounds = new Rect();
+ mPipBoundsAlgorithm.getInsetBounds(insetBounds);
+ overrideDefaultStackGravity(Gravity.END | Gravity.BOTTOM);
+
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ assertEquals(insetBounds.bottom, defaultBounds.bottom);
+ assertEquals(insetBounds.right, defaultBounds.right);
+ }
+
+ @Test
+ public void getNormalBounds_invalidAspectRatio_returnsDefaultBounds() {
+ final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
+
+ // Set an invalid current aspect ratio.
+ mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO / 2);
+ final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds();
+
+ assertEquals(defaultBounds, normalBounds);
+ }
+
+ @Test
+ public void getNormalBounds_validAspectRatio_returnsAdjustedDefaultBounds() {
+ final Rect defaultBoundsAdjustedToAspectRatio = mPipBoundsAlgorithm.getDefaultBounds();
+ mPipBoundsAlgorithm.transformBoundsToAspectRatio(defaultBoundsAdjustedToAspectRatio,
+ MIN_ASPECT_RATIO, false /* useCurrentMinEdgeSize */, false /* useCurrentSize */);
+
+ // Set a valid current aspect ratio different that the default.
+ mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO);
+ final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds();
+
+ assertEquals(defaultBoundsAdjustedToAspectRatio, normalBounds);
+ }
+
+ @Test
public void getEntryDestinationBounds_returnBoundsMatchesAspectRatio() {
final float[] aspectRatios = new float[] {
(MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2,
@@ -120,9 +243,8 @@ public class PipBoundsHandlerTest extends ShellTestCase {
};
for (float aspectRatio : aspectRatios) {
mPipBoundsState.setAspectRatio(aspectRatio);
- final Rect destinationBounds = mPipBoundsHandler.getEntryDestinationBounds();
- final float actualAspectRatio =
- destinationBounds.width() / (destinationBounds.height() * 1f);
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+ final float actualAspectRatio = getRectAspectRatio(destinationBounds);
assertEquals("Destination bounds matches the given aspect ratio",
aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN);
}
@@ -136,11 +258,11 @@ public class PipBoundsHandlerTest extends ShellTestCase {
};
for (float aspectRatio : invalidAspectRatios) {
mPipBoundsState.setAspectRatio(aspectRatio);
- final Rect destinationBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
assertEquals("Destination bounds fallbacks to default aspect ratio",
- mPipBoundsHandler.getDefaultAspectRatio(), actualAspectRatio,
+ mPipBoundsAlgorithm.getDefaultAspectRatio(), actualAspectRatio,
ASPECT_RATIO_ERROR_MARGIN);
}
}
@@ -152,7 +274,7 @@ public class PipBoundsHandlerTest extends ShellTestCase {
currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
mPipBoundsState.setAspectRatio(aspectRatio);
- final Rect destinationBounds = mPipBoundsHandler.getAdjustedDestinationBounds(
+ final Rect destinationBounds = mPipBoundsAlgorithm.getAdjustedDestinationBounds(
currentBounds, aspectRatio);
final float actualAspectRatio =
@@ -178,7 +300,7 @@ public class PipBoundsHandlerTest extends ShellTestCase {
final Size minimalSize = minimalSizes[i];
mPipBoundsState.setAspectRatio(aspectRatio);
mPipBoundsState.setOverrideMinSize(minimalSize);
- final Rect destinationBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
assertTrue("Destination bounds is no smaller than minimal requirement",
(destinationBounds.width() == minimalSize.getWidth()
&& destinationBounds.height() >= minimalSize.getHeight())
@@ -200,7 +322,7 @@ public class PipBoundsHandlerTest extends ShellTestCase {
mPipBoundsState.setAspectRatio(aspectRatio);
mPipBoundsState.setOverrideMinSize(minSize);
- final Rect destinationBounds = mPipBoundsHandler.getAdjustedDestinationBounds(
+ final Rect destinationBounds = mPipBoundsAlgorithm.getAdjustedDestinationBounds(
currentBounds, aspectRatio);
assertTrue("Destination bounds ignores minimal size",
@@ -211,12 +333,12 @@ public class PipBoundsHandlerTest extends ShellTestCase {
@Test
public void getEntryDestinationBounds_reentryStateExists_restoreLastSize() {
mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
- final Rect reentryBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect reentryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
reentryBounds.scale(1.25f);
- final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds);
+ final float reentrySnapFraction = mPipBoundsAlgorithm.getSnapFraction(reentryBounds);
mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction);
- final Rect destinationBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
assertEquals(reentryBounds.width(), destinationBounds.width());
assertEquals(reentryBounds.height(), destinationBounds.height());
@@ -225,13 +347,13 @@ public class PipBoundsHandlerTest extends ShellTestCase {
@Test
public void getEntryDestinationBounds_reentryStateExists_restoreLastPosition() {
mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
- final Rect reentryBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect reentryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
reentryBounds.offset(0, -100);
- final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds);
+ final float reentrySnapFraction = mPipBoundsAlgorithm.getSnapFraction(reentryBounds);
mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction);
- final Rect destinationBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
assertBoundsInclusionWithMargin("restoreLastPosition", reentryBounds, destinationBounds);
}
@@ -240,10 +362,10 @@ public class PipBoundsHandlerTest extends ShellTestCase {
public void setShelfHeight_offsetBounds() {
final int shelfHeight = 100;
mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
- final Rect oldPosition = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect oldPosition = mPipBoundsAlgorithm.getEntryDestinationBounds();
mPipBoundsState.setShelfVisibility(true, shelfHeight);
- final Rect newPosition = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect newPosition = mPipBoundsAlgorithm.getEntryDestinationBounds();
oldPosition.offset(0, -shelfHeight);
assertBoundsInclusionWithMargin("offsetBounds by shelf", oldPosition, newPosition);
@@ -253,10 +375,10 @@ public class PipBoundsHandlerTest extends ShellTestCase {
public void onImeVisibilityChanged_offsetBounds() {
final int imeHeight = 100;
mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
- final Rect oldPosition = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect oldPosition = mPipBoundsAlgorithm.getEntryDestinationBounds();
mPipBoundsState.setImeVisibility(true, imeHeight);
- final Rect newPosition = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect newPosition = mPipBoundsAlgorithm.getEntryDestinationBounds();
oldPosition.offset(0, -imeHeight);
assertBoundsInclusionWithMargin("offsetBounds by IME", oldPosition, newPosition);
@@ -265,15 +387,31 @@ public class PipBoundsHandlerTest extends ShellTestCase {
@Test
public void getEntryDestinationBounds_noReentryState_useDefaultBounds() {
mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
- final Rect defaultBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect defaultBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
mPipBoundsState.clearReentryState();
- final Rect actualBounds = mPipBoundsHandler.getEntryDestinationBounds();
+ final Rect actualBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds);
}
+ private void overrideDefaultAspectRatio(float aspectRatio) {
+ final TestableResources res = mContext.getOrCreateTestableResources();
+ res.addOverride(
+ com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio,
+ aspectRatio);
+ mPipBoundsAlgorithm.onConfigurationChanged(mContext);
+ }
+
+ private void overrideDefaultStackGravity(int stackGravity) {
+ final TestableResources res = mContext.getOrCreateTestableResources();
+ res.addOverride(
+ com.android.internal.R.integer.config_defaultPictureInPictureGravity,
+ stackGravity);
+ mPipBoundsAlgorithm.onConfigurationChanged(mContext);
+ }
+
private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) {
final Rect expectedWithMargin = new Rect(expected);
expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
@@ -282,4 +420,8 @@ public class PipBoundsHandlerTest extends ShellTestCase {
+ " with error margin " + ROUNDING_ERROR_MARGIN,
expectedWithMargin.contains(actual));
}
+
+ private static float getRectAspectRatio(Rect rect) {
+ return rect.width() / (rect.height() * 1f);
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
new file mode 100644
index 000000000000..dcee2e1847b2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.pip;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link PipSnapAlgorithm}. **/
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipSnapAlgorithmTest extends ShellTestCase {
+ private static final int DEFAULT_STASH_OFFSET = 32;
+ private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 2000, 2000);
+ private static final Rect STACK_BOUNDS_CENTERED = new Rect(900, 900, 1100, 1100);
+ private static final Rect MOVEMENT_BOUNDS = new Rect(0, 0,
+ DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width(),
+ DISPLAY_BOUNDS.width() - STACK_BOUNDS_CENTERED.width());
+
+ private PipSnapAlgorithm mPipSnapAlgorithm;
+
+ @Before
+ public void setUp() {
+ mPipSnapAlgorithm = new PipSnapAlgorithm();
+ }
+
+ @Test
+ public void testApplySnapFraction_topEdge() {
+ final float snapFraction = 0.25f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+ assertEquals(MOVEMENT_BOUNDS.width() / 4, bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.top, bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_rightEdge() {
+ final float snapFraction = 1.5f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+ assertEquals(MOVEMENT_BOUNDS.right, bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.height() / 2, bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_bottomEdge() {
+ final float snapFraction = 2.25f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+ assertEquals((int) (MOVEMENT_BOUNDS.width() * 0.75f), bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_leftEdge() {
+ final float snapFraction = 3.75f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction);
+
+ assertEquals(MOVEMENT_BOUNDS.left, bounds.left);
+ assertEquals((int) (MOVEMENT_BOUNDS.height() * 0.25f), bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_notStashed_isNotOffBounds() {
+ final float snapFraction = 2f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction,
+ PipBoundsState.STASH_TYPE_NONE, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+ assertEquals(MOVEMENT_BOUNDS.right, bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_stashedLeft() {
+ final float snapFraction = 3f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction,
+ PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+ final int offBoundsWidth = bounds.width() - DEFAULT_STASH_OFFSET;
+ assertEquals(MOVEMENT_BOUNDS.left - offBoundsWidth, bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+ }
+
+ @Test
+ public void testApplySnapFraction_stashedRight() {
+ final float snapFraction = 2f;
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, snapFraction,
+ PipBoundsState.STASH_TYPE_RIGHT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+ assertEquals(DISPLAY_BOUNDS.right - DEFAULT_STASH_OFFSET, bounds.left);
+ assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+ }
+
+ @Test
+ public void testSnapRectToClosestEdge_rightEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move the centered rect slightly to the right side.
+ bounds.offset(10, 0);
+
+ mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+ PipBoundsState.STASH_TYPE_NONE);
+
+ assertEquals(MOVEMENT_BOUNDS.right, bounds.left);
+ }
+
+ @Test
+ public void testSnapRectToClosestEdge_leftEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move the centered rect slightly to the left side.
+ bounds.offset(-10, 0);
+
+ mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+ PipBoundsState.STASH_TYPE_NONE);
+
+ assertEquals(MOVEMENT_BOUNDS.left, bounds.left);
+ }
+
+ @Test
+ public void testSnapRectToClosestEdge_topEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move the centered rect slightly to the top half.
+ bounds.offset(0, -10);
+
+ mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+ PipBoundsState.STASH_TYPE_NONE);
+
+ assertEquals(MOVEMENT_BOUNDS.top, bounds.top);
+ }
+
+ @Test
+ public void testSnapRectToClosestEdge_bottomEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move the centered rect slightly to the bottom half.
+ bounds.offset(0, 10);
+
+ mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+ PipBoundsState.STASH_TYPE_NONE);
+
+ assertEquals(MOVEMENT_BOUNDS.bottom, bounds.top);
+ }
+
+ @Test
+ public void testSnapRectToClosestEdge_stashed_unStahesBounds() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Stash it on the left side.
+ mPipSnapAlgorithm.applySnapFraction(bounds, MOVEMENT_BOUNDS, 3.5f,
+ PipBoundsState.STASH_TYPE_LEFT, DEFAULT_STASH_OFFSET, DISPLAY_BOUNDS);
+
+ mPipSnapAlgorithm.snapRectToClosestEdge(bounds, MOVEMENT_BOUNDS, bounds,
+ PipBoundsState.STASH_TYPE_LEFT);
+
+ assertEquals(MOVEMENT_BOUNDS.left, bounds.left);
+ }
+
+ @Test
+ public void testGetSnapFraction_leftEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move it slightly to the left side.
+ bounds.offset(-10, 0);
+
+ final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+ assertEquals(3.5f, snapFraction, 0.1f);
+ }
+
+ @Test
+ public void testGetSnapFraction_rightEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move it slightly to the right side.
+ bounds.offset(10, 0);
+
+ final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+ assertEquals(1.5f, snapFraction, 0.1f);
+ }
+
+ @Test
+ public void testGetSnapFraction_topEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move it slightly to the top half.
+ bounds.offset(0, -10);
+
+ final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+ assertEquals(0.5f, snapFraction, 0.1f);
+ }
+
+ @Test
+ public void testGetSnapFraction_bottomEdge() {
+ final Rect bounds = new Rect(STACK_BOUNDS_CENTERED);
+ // Move it slightly to the bottom half.
+ bounds.offset(0, 10);
+
+ final float snapFraction = mPipSnapAlgorithm.getSnapFraction(bounds, MOVEMENT_BOUNDS);
+
+ assertEquals(2.5f, snapFraction, 0.1f);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 08841bd2b1b2..8d3774cee1e0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -65,7 +65,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
private PipTaskOrganizer mSpiedPipTaskOrganizer;
@Mock private DisplayController mMockdDisplayController;
- @Mock private PipBoundsHandler mMockPipBoundsHandler;
+ @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
@Mock private PipMenuActivityController mMenuActivityController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
@@ -83,7 +83,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mComponent2 = new ComponentName(mContext, "component2");
mPipBoundsState = new PipBoundsState(mContext);
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState,
- mMockPipBoundsHandler, mMenuActivityController, mMockPipSurfaceTransactionHelper,
+ mMockPipBoundsAlgorithm, mMenuActivityController, mMockPipSurfaceTransactionHelper,
mMockOptionalSplitScreen, mMockdDisplayController, mMockPipUiEventLogger,
mMockShellTaskOrganizer));
preparePipTaskOrg();
@@ -192,8 +192,8 @@ public class PipTaskOrganizerTest extends ShellTestCase {
private void preparePipTaskOrg() {
final DisplayInfo info = new DisplayInfo();
mPipBoundsState.setDisplayInfo(info);
- when(mMockPipBoundsHandler.getEntryDestinationBounds()).thenReturn(new Rect());
- when(mMockPipBoundsHandler.getAdjustedDestinationBounds(any(), anyFloat()))
+ when(mMockPipBoundsAlgorithm.getEntryDestinationBounds()).thenReturn(new Rect());
+ when(mMockPipBoundsAlgorithm.getAdjustedDestinationBounds(any(), anyFloat()))
.thenReturn(new Rect());
mPipBoundsState.setDisplayInfo(info);
mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 745d18804f0b..88c8eb902a6f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -40,7 +40,7 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.pip.PipBoundsHandler;
+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;
@@ -63,7 +63,7 @@ public class PipControllerTest extends ShellTestCase {
@Mock private DisplayController mMockDisplayController;
@Mock private PipMenuActivityController mMockPipMenuActivityController;
@Mock private PipAppOpsListener mMockPipAppOpsListener;
- @Mock private PipBoundsHandler mMockPipBoundsHandler;
+ @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
@Mock private PipMediaController mMockPipMediaController;
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
@Mock private PipTouchHandler mMockPipTouchHandler;
@@ -76,7 +76,7 @@ public class PipControllerTest extends ShellTestCase {
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mPipController = new PipController(mContext, mMockDisplayController,
- mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState,
+ mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener,
mMockExecutor);
@@ -109,7 +109,7 @@ public class PipControllerTest extends ShellTestCase {
when(spyContext.getPackageManager()).thenReturn(mockPackageManager);
assertNull(PipController.create(spyContext, mMockDisplayController,
- mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState,
+ mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipBoundsState,
mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
mMockPipTouchHandler, mMockWindowManagerShellWrapper, mMockTaskStackListener,
mMockExecutor));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 94f30511ebbf..abbc681f53fe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -33,7 +33,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.FloatingContentCoordinator;
-import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -72,7 +72,7 @@ public class PipTouchHandlerTest extends ShellTestCase {
private PipUiEventLogger mPipUiEventLogger;
private PipBoundsState mPipBoundsState;
- private PipBoundsHandler mPipBoundsHandler;
+ private PipBoundsAlgorithm mPipBoundsAlgorithm;
private PipSnapAlgorithm mPipSnapAlgorithm;
private PipMotionHelper mMotionHelper;
private PipResizeGestureHandler mPipResizeGestureHandler;
@@ -89,12 +89,12 @@ public class PipTouchHandlerTest extends ShellTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mPipBoundsState = new PipBoundsState(mContext);
- mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState);
- mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm();
- mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
+ mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState);
+ mPipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
+ mPipSnapAlgorithm = new PipSnapAlgorithm();
mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController,
- mPipBoundsHandler, mPipBoundsState, mPipTaskOrganizer, mFloatingContentCoordinator,
- mPipUiEventLogger);
+ mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer,
+ mFloatingContentCoordinator, mPipUiEventLogger);
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
mPipTouchHandler.setPipMotionHelper(mMotionHelper);
@@ -115,12 +115,13 @@ public class PipTouchHandlerTest extends ShellTestCase {
@Test
public void updateMovementBounds_minBounds() {
Rect expectedMinMovementBounds = new Rect();
- mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0);
+ mPipBoundsAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds,
+ 0);
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
- assertEquals(expectedMinMovementBounds, mPipTouchHandler.mNormalMovementBounds);
+ assertEquals(expectedMinMovementBounds, mPipBoundsState.getNormalMovementBounds());
verify(mPipResizeGestureHandler, times(1))
.updateMinSize(mMinBounds.width(), mMinBounds.height());
}
@@ -129,17 +130,18 @@ public class PipTouchHandlerTest extends ShellTestCase {
public void updateMovementBounds_maxBounds() {
Point displaySize = new Point();
mContext.getDisplay().getRealSize(displaySize);
- Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1,
+ Size maxSize = mPipBoundsAlgorithm.getSizeForAspectRatio(1,
mContext.getResources().getDimensionPixelSize(
R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y);
Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight());
Rect expectedMaxMovementBounds = new Rect();
- mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0);
+ mPipBoundsAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds,
+ 0);
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
- assertEquals(expectedMaxMovementBounds, mPipTouchHandler.mExpandedMovementBounds);
+ assertEquals(expectedMaxMovementBounds, mPipBoundsState.getExpandedMovementBounds());
verify(mPipResizeGestureHandler, times(1))
.updateMaxSize(maxBounds.width(), maxBounds.height());
}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index a3fcf90b2d46..8330363f3c84 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -61,6 +61,9 @@ cc_library {
],
export_include_dirs: ["include"],
export_shared_lib_headers: ["libz"],
+ static_libs: ["libincfs-utils"],
+ whole_static_libs: ["libincfs-utils"],
+ export_static_lib_headers: ["libincfs-utils"],
target: {
android: {
srcs: [
@@ -69,13 +72,14 @@ cc_library {
"CursorWindow.cpp",
],
shared_libs: [
- "libziparchive",
"libbase",
"libbinder",
"liblog",
"libcutils",
+ "libincfs",
"libutils",
"libz",
+ "libziparchive",
],
static: {
enabled: false,
@@ -86,11 +90,11 @@ cc_library {
enabled: false,
},
static_libs: [
- "libziparchive",
"libbase",
- "liblog",
"libcutils",
+ "liblog",
"libutils",
+ "libziparchive",
],
shared_libs: [
"libz",
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index e15b42d46f53..cb56a5172a45 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -25,13 +25,11 @@
#include "android-base/unique_fd.h"
#include "android-base/utf8.h"
#include "utils/Compat.h"
-#include "utils/FileMap.h"
#include "ziparchive/zip_archive.h"
#include "androidfw/Asset.h"
#include "androidfw/Idmap.h"
#include "androidfw/misc.h"
-#include "androidfw/ResourceTypes.h"
#include "androidfw/Util.h"
namespace android {
@@ -161,50 +159,46 @@ class ZipAssetsProvider : public AssetsProvider {
}
const int fd = ::GetFileDescriptor(zip_handle_.get());
- const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
+ const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
+ incfs::IncFsFileMap asset_map;
if (entry.method == kCompressDeflated) {
- std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
- if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length,
- true /*readOnly*/)) {
+ if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) {
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
return {};
}
std::unique_ptr<Asset> asset =
- Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
+ Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
if (asset == nullptr) {
LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
return {};
}
return asset;
- } else {
- std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
- if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.uncompressed_length,
- true /*readOnly*/)) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
+ }
- unique_fd ufd;
- if (!GetPath()) {
- // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
- // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
- // to create new file descriptors.
- ufd = unique_fd(dup(fd));
- if (!ufd.ok()) {
- LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
- return {};
- }
- }
+ if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
- std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map),
- std::move(ufd), mode);
- if (asset == nullptr) {
- LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ unique_fd ufd;
+ if (!GetPath()) {
+ // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
+ // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
+ // to create new file descriptors.
+ ufd = unique_fd(dup(fd));
+ if (!ufd.ok()) {
+ LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
return {};
}
- return asset;
}
+
+ auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
+ if (asset == nullptr) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
+ return {};
+ }
+ return asset;
}
private:
@@ -446,8 +440,8 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
}
}
- std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
- if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) {
+ incfs::IncFsFileMap file_map;
+ if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
<< SystemErrorCodeToString(errno);
return {};
@@ -456,8 +450,8 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
// If `path` is set, do not pass ownership of the `fd` to the new Asset since
// Asset::openFileDescriptor can use `path` to create new file descriptors.
return Asset::createFromUncompressedMap(std::move(file_map),
- (path) ? base::unique_fd(-1) : std::move(fd),
- Asset::AccessMode::ACCESS_RANDOM);
+ Asset::AccessMode::ACCESS_RANDOM,
+ (path) ? base::unique_fd(-1) : std::move(fd));
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
@@ -493,15 +487,14 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
loaded_apk->idmap_asset_ = std::move(idmap_asset);
loaded_apk->loaded_idmap_ = std::move(idmap);
- const StringPiece data(
- reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
- loaded_apk->resources_asset_->getLength());
- if (data.data() == nullptr || data.empty()) {
+ const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
+ const size_t length = loaded_apk->resources_asset_->getLength();
+ if (!data || length == 0) {
LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'.";
return {};
}
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(),
property_flags);
if (!loaded_apk->loaded_arsc_) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
@@ -525,15 +518,15 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(
new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
loaded_apk->resources_asset_ = std::move(resources_asset);
- const StringPiece data(
- reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
- loaded_apk->resources_asset_->getLength());
- if (data.data() == nullptr || data.empty()) {
+ const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
+ const size_t length = loaded_apk->resources_asset_->getLength();
+ if (!data || length == 0) {
LOG(ERROR) << "Failed to read resources table data in '" << path << "'.";
return {};
}
- loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */,
+ property_flags);
if (loaded_apk->loaded_arsc_ == nullptr) {
LOG(ERROR) << "Failed to read resources table in '" << path << "'.";
return {};
@@ -550,7 +543,6 @@ bool ApkAssets::IsUpToDate() const {
}
return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) &&
last_mod_time_ == getFileModDate(path_.c_str());
-
}
} // namespace android
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index cd30c184d5a4..4fbe4a3efbdd 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -298,34 +298,18 @@ Asset::Asset(void)
/*
* Create a new Asset from a memory mapping.
*/
-/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode)
+/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(incfs::IncFsFileMap&& dataMap,
+ AccessMode mode,
+ base::unique_fd fd)
{
- _FileAsset* pAsset;
- status_t result;
-
- pAsset = new _FileAsset;
- result = pAsset->openChunk(dataMap, base::unique_fd(-1));
- if (result != NO_ERROR) {
- delete pAsset;
- return NULL;
- }
-
- pAsset->mAccessMode = mode;
- return pAsset;
-}
+ auto pAsset = util::make_unique<_FileAsset>();
-/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
- base::unique_fd fd, AccessMode mode)
-{
- std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>();
-
- status_t result = pAsset->openChunk(dataMap.get(), std::move(fd));
+ status_t result = pAsset->openChunk(std::move(dataMap), std::move(fd));
if (result != NO_ERROR) {
return NULL;
}
// We succeeded, so relinquish control of dataMap
- (void) dataMap.release();
pAsset->mAccessMode = mode;
return std::move(pAsset);
}
@@ -333,35 +317,18 @@ Asset::Asset(void)
/*
* Create a new Asset from compressed data in a memory mapping.
*/
-/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
- size_t uncompressedLen, AccessMode mode)
+/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(incfs::IncFsFileMap&& dataMap,
+ size_t uncompressedLen,
+ AccessMode mode)
{
- _CompressedAsset* pAsset;
- status_t result;
-
- pAsset = new _CompressedAsset;
- result = pAsset->openChunk(dataMap, uncompressedLen);
- if (result != NO_ERROR) {
- delete pAsset;
- return NULL;
- }
+ auto pAsset = util::make_unique<_CompressedAsset>();
- pAsset->mAccessMode = mode;
- return pAsset;
-}
-
-/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
- size_t uncompressedLen, AccessMode mode)
-{
- std::unique_ptr<_CompressedAsset> pAsset = util::make_unique<_CompressedAsset>();
-
- status_t result = pAsset->openChunk(dataMap.get(), uncompressedLen);
+ status_t result = pAsset->openChunk(std::move(dataMap), uncompressedLen);
if (result != NO_ERROR) {
return NULL;
}
// We succeeded, so relinquish control of dataMap
- (void) dataMap.release();
pAsset->mAccessMode = mode;
return std::move(pAsset);
}
@@ -414,7 +381,7 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m
* Constructor.
*/
_FileAsset::_FileAsset(void)
- : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mMap(NULL), mBuf(NULL)
+ : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mBuf(NULL)
{
// Register the Asset with the global list here after it is fully constructed and its
// vtable pointer points to this concrete type. b/31113965
@@ -441,7 +408,7 @@ _FileAsset::~_FileAsset(void)
status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
{
assert(mFp == NULL); // no reopen
- assert(mMap == NULL);
+ assert(!mMap.has_value());
assert(fd >= 0);
assert(offset >= 0);
@@ -484,15 +451,15 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, siz
/*
* Create the chunk from the map.
*/
-status_t _FileAsset::openChunk(FileMap* dataMap, base::unique_fd fd)
+status_t _FileAsset::openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd)
{
assert(mFp == NULL); // no reopen
- assert(mMap == NULL);
+ assert(!mMap.has_value());
assert(dataMap != NULL);
- mMap = dataMap;
+ mMap = std::move(dataMap);
mStart = -1; // not used
- mLength = dataMap->getDataLength();
+ mLength = mMap->length();
mFd = std::move(fd);
assert(mOffset == 0);
@@ -528,10 +495,15 @@ ssize_t _FileAsset::read(void* buf, size_t count)
if (!count)
return 0;
- if (mMap != NULL) {
+ if (mMap.has_value()) {
/* copy from mapped area */
//printf("map read\n");
- memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
+ const auto readPos = mMap->data().offset(mOffset).convert<char>();
+ if (!readPos.verify(count)) {
+ return -1;
+ }
+
+ memcpy(buf, readPos.unsafe_ptr(), count);
actual = count;
} else if (mBuf != NULL) {
/* copy from buffer */
@@ -594,10 +566,6 @@ off64_t _FileAsset::seek(off64_t offset, int whence)
*/
void _FileAsset::close(void)
{
- if (mMap != NULL) {
- delete mMap;
- mMap = NULL;
- }
if (mBuf != NULL) {
delete[] mBuf;
mBuf = NULL;
@@ -624,16 +592,21 @@ void _FileAsset::close(void)
* level and we'd be using a different object, but we didn't, so we
* deal with it here.
*/
-const void* _FileAsset::getBuffer(bool wordAligned)
+const void* _FileAsset::getBuffer(bool aligned)
+{
+ return getIncFsBuffer(aligned).unsafe_ptr();
+}
+
+incfs::map_ptr<void> _FileAsset::getIncFsBuffer(bool aligned)
{
/* subsequent requests just use what we did previously */
if (mBuf != NULL)
return mBuf;
- if (mMap != NULL) {
- if (!wordAligned) {
- return mMap->getDataPtr();
+ if (mMap.has_value()) {
+ if (!aligned) {
+ return mMap->data();
}
- return ensureAlignment(mMap);
+ return ensureAlignment(*mMap);
}
assert(mFp != NULL);
@@ -671,47 +644,44 @@ const void* _FileAsset::getBuffer(bool wordAligned)
mBuf = buf;
return mBuf;
} else {
- FileMap* map;
-
- map = new FileMap;
- if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
- delete map;
+ incfs::IncFsFileMap map;
+ if (!map.Create(fileno(mFp), mStart, mLength, NULL /* file_name */ )) {
return NULL;
}
ALOGV(" getBuffer: mapped\n");
- mMap = map;
- if (!wordAligned) {
- return mMap->getDataPtr();
+ mMap = std::move(map);
+ if (!aligned) {
+ return mMap->data();
}
- return ensureAlignment(mMap);
+ return ensureAlignment(*mMap);
}
}
int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
{
- if (mMap != NULL) {
+ if (mMap.has_value()) {
if (mFd.ok()) {
- *outStart = mMap->getDataOffset();
- *outLength = mMap->getDataLength();
- const int fd = dup(mFd);
- if (fd < 0) {
- ALOGE("Unable to dup fd (%d).", mFd.get());
- return -1;
- }
- lseek64(fd, 0, SEEK_SET);
- return fd;
+ *outStart = mMap->offset();
+ *outLength = mMap->length();
+ const int fd = dup(mFd);
+ if (fd < 0) {
+ ALOGE("Unable to dup fd (%d).", mFd.get());
+ return -1;
+ }
+ lseek64(fd, 0, SEEK_SET);
+ return fd;
}
- const char* fname = mMap->getFileName();
+ const char* fname = mMap->file_name();
if (fname == NULL) {
fname = mFileName;
}
if (fname == NULL) {
return -1;
}
- *outStart = mMap->getDataOffset();
- *outLength = mMap->getDataLength();
+ *outStart = mMap->offset();
+ *outLength = mMap->length();
return open(fname, O_RDONLY | O_BINARY);
}
if (mFileName == NULL) {
@@ -722,16 +692,21 @@ int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
return open(mFileName, O_RDONLY | O_BINARY);
}
-const void* _FileAsset::ensureAlignment(FileMap* map)
+incfs::map_ptr<void> _FileAsset::ensureAlignment(const incfs::IncFsFileMap& map)
{
- void* data = map->getDataPtr();
- if ((((size_t)data)&0x3) == 0) {
+ const auto data = map.data();
+ if (util::IsFourByteAligned(data)) {
// We can return this directly if it is aligned on a word
// boundary.
ALOGV("Returning aligned FileAsset %p (%s).", this,
getAssetSource());
return data;
}
+
+ if (!data.convert<uint8_t>().verify(mLength)) {
+ return NULL;
+ }
+
// If not aligned on a word boundary, then we need to copy it into
// our own buffer.
ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
@@ -741,7 +716,8 @@ const void* _FileAsset::ensureAlignment(FileMap* map)
ALOGE("alloc of %ld bytes failed\n", (long) mLength);
return NULL;
}
- memcpy(buf, data, mLength);
+
+ memcpy(buf, data.unsafe_ptr(), mLength);
mBuf = buf;
return buf;
}
@@ -757,7 +733,7 @@ const void* _FileAsset::ensureAlignment(FileMap* map)
*/
_CompressedAsset::_CompressedAsset(void)
: mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
- mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
+ mFd(-1), mZipInflater(NULL), mBuf(NULL)
{
// Register the Asset with the global list here after it is fully constructed and its
// vtable pointer points to this concrete type. b/31113965
@@ -786,7 +762,7 @@ status_t _CompressedAsset::openChunk(int fd, off64_t offset,
int compressionMethod, size_t uncompressedLen, size_t compressedLen)
{
assert(mFd < 0); // no re-open
- assert(mMap == NULL);
+ assert(!mMap.has_value());
assert(fd >= 0);
assert(offset >= 0);
assert(compressedLen > 0);
@@ -815,20 +791,20 @@ status_t _CompressedAsset::openChunk(int fd, off64_t offset,
*
* Nothing is expanded until the first read call.
*/
-status_t _CompressedAsset::openChunk(FileMap* dataMap, size_t uncompressedLen)
+status_t _CompressedAsset::openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen)
{
assert(mFd < 0); // no re-open
- assert(mMap == NULL);
+ assert(!mMap.has_value());
assert(dataMap != NULL);
- mMap = dataMap;
+ mMap = std::move(dataMap);
mStart = -1; // not used
- mCompressedLen = dataMap->getDataLength();
+ mCompressedLen = mMap->length();
mUncompressedLen = uncompressedLen;
assert(mOffset == 0);
if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
- mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
+ mZipInflater = new StreamingZipInflater(&(*mMap), uncompressedLen);
}
return NO_ERROR;
}
@@ -901,11 +877,6 @@ off64_t _CompressedAsset::seek(off64_t offset, int whence)
*/
void _CompressedAsset::close(void)
{
- if (mMap != NULL) {
- delete mMap;
- mMap = NULL;
- }
-
delete[] mBuf;
mBuf = NULL;
@@ -940,8 +911,8 @@ const void* _CompressedAsset::getBuffer(bool)
goto bail;
}
- if (mMap != NULL) {
- if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
+ if (mMap.has_value()) {
+ if (!ZipUtils::inflateToBuffer(mMap->data(), buf,
mUncompressedLen, mCompressedLen))
goto bail;
} else {
@@ -976,3 +947,6 @@ bail:
return mBuf;
}
+incfs::map_ptr<void> _CompressedAsset::getIncFsBuffer(bool aligned) {
+ return incfs::map_ptr<void>(getBuffer(aligned));
+}
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index f7c83371f79c..fb2b57193b83 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -917,7 +917,7 @@ Asset* AssetManager::openAssetFromFileLocked(const String8& pathName,
Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile,
const ZipEntryRO entry, AccessMode mode, const String8& entryName)
{
- Asset* pAsset = NULL;
+ std::unique_ptr<Asset> pAsset;
// TODO: look for previously-created shared memory slice?
uint16_t method;
@@ -932,28 +932,28 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile,
return NULL;
}
- FileMap* dataMap = pZipFile->createEntryFileMap(entry);
- if (dataMap == NULL) {
+ std::optional<incfs::IncFsFileMap> dataMap = pZipFile->createEntryIncFsFileMap(entry);
+ if (!dataMap.has_value()) {
ALOGW("create map from entry failed\n");
return NULL;
}
if (method == ZipFileRO::kCompressStored) {
- pAsset = Asset::createFromUncompressedMap(dataMap, mode);
+ pAsset = Asset::createFromUncompressedMap(std::move(*dataMap), mode);
ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(),
- dataMap->getFileName(), mode, pAsset);
+ dataMap->file_name(), mode, pAsset.get());
} else {
- pAsset = Asset::createFromCompressedMap(dataMap,
+ pAsset = Asset::createFromCompressedMap(std::move(*dataMap),
static_cast<size_t>(uncompressedLen), mode);
ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(),
- dataMap->getFileName(), mode, pAsset);
+ dataMap->file_name(), mode, pAsset.get());
}
if (pAsset == NULL) {
/* unexpected */
ALOGW("create from segment failed\n");
}
- return pAsset;
+ return pAsset.release();
}
/*
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 99dd3134ff8a..8c2a632b9fd7 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -38,9 +38,43 @@
namespace android {
+namespace {
+
+using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>;
+
+base::expected<EntryValue, IOError> GetEntryValue(
+ incfs::verified_map_ptr<ResTable_entry> table_entry) {
+ const uint16_t entry_size = dtohs(table_entry->size);
+
+ // Check if the entry represents a bag value.
+ if (entry_size >= sizeof(ResTable_map_entry) &&
+ (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
+ const auto map_entry = table_entry.convert<ResTable_map_entry>();
+ if (!map_entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ return map_entry.verified();
+ }
+
+ // The entry represents a non-bag value.
+ const auto entry_value = table_entry.offset(entry_size).convert<Res_value>();
+ if (!entry_value) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ Res_value value;
+ value.copyFrom_dtoh(entry_value.value());
+ return value;
+}
+
+} // namespace
+
struct FindEntryResult {
- // A pointer to the value of the resource table entry.
- std::variant<Res_value, const ResTable_map_entry*> entry;
+ // The cookie representing the ApkAssets in which the value resides.
+ ApkAssetsCookie cookie;
+
+ // The value of the resource table entry. Either an android::Res_value for non-bag types or an
+ // incfs::verified_map_ptr<ResTable_map_entry> for bag types.
+ EntryValue entry;
// The configuration for which the resulting entry was defined. This is already swapped to host
// endianness.
@@ -265,7 +299,7 @@ const std::unordered_map<std::string, std::string>*
}
const PackageGroup& package_group = package_groups_[idx];
- if (package_group.packages_.size() == 0) {
+ if (package_group.packages_.empty()) {
return nullptr;
}
@@ -310,14 +344,14 @@ bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_
for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) {
const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it);
if (info != nullptr) {
- ResourceName res_name;
- if (!GetResourceName(*it, &res_name)) {
+ auto res_name = GetResourceName(*it);
+ if (!res_name.has_value()) {
ANDROID_LOG(ERROR) << base::StringPrintf(
"Unable to retrieve name of overlayable resource 0x%08x", *it);
return false;
}
- const std::string name = ToFormattedResourceString(&res_name);
+ const std::string name = ToFormattedResourceString(*res_name);
output.append(base::StringPrintf(
"resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n",
name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags));
@@ -365,8 +399,8 @@ std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const {
return non_system_overlays;
}
-std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
- bool exclude_mipmap) const {
+base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceConfigurations(
+ bool exclude_system, bool exclude_mipmap) const {
ATRACE_NAME("AssetManager::GetResourceConfigurations");
const auto non_system_overlays =
(exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
@@ -386,7 +420,10 @@ std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_
continue;
}
- package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
+ auto result = package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
+ if (UNLIKELY(!result.has_value())) {
+ return base::unexpected(result.error());
+ }
}
}
return configurations;
@@ -501,11 +538,11 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
return apk_assets_[cookie]->GetAssetsProvider()->Open(filename, mode);
}
-ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
- bool /*stop_at_first_match*/,
- bool ignore_configuration,
- FindEntryResult* out_entry) const {
- if (resource_resolution_logging_enabled_) {
+base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
+ uint32_t resid, uint16_t density_override, bool stop_at_first_match,
+ bool ignore_configuration) const {
+ const bool logging_enabled = resource_resolution_logging_enabled_;
+ if (UNLIKELY(logging_enabled)) {
// Clear the last logged resource resolution.
ResetResourceResolution();
last_resolution_.resid = resid;
@@ -523,94 +560,96 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
}
// Retrieve the package group from the package id of the resource id.
- if (!is_valid_resid(resid)) {
+ if (UNLIKELY(!is_valid_resid(resid))) {
LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
- return kInvalidCookie;
+ return base::unexpected(std::nullopt);
}
const uint32_t package_id = get_package_id(resid);
const uint8_t type_idx = get_type_id(resid) - 1;
const uint16_t entry_idx = get_entry_id(resid);
uint8_t package_idx = package_ids_[package_id];
- if (package_idx == 0xff) {
+ if (UNLIKELY(package_idx == 0xff)) {
ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
package_id, resid);
- return kInvalidCookie;
+ return base::unexpected(std::nullopt);
}
const PackageGroup& package_group = package_groups_[package_idx];
- ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
- false /* stop_at_first_match */,
- ignore_configuration, out_entry);
- if (UNLIKELY(cookie == kInvalidCookie)) {
- return kInvalidCookie;
+ auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
+ stop_at_first_match, ignore_configuration);
+ if (UNLIKELY(!result.has_value())) {
+ return base::unexpected(result.error());
}
- if (!apk_assets_[cookie]->IsLoader()) {
+ 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);
if (!overlay_entry) {
// No id map entry exists for this target resource.
continue;
- } else if (overlay_entry.IsInlineValue()) {
+ }
+ if (overlay_entry.IsInlineValue()) {
// The target resource is overlaid by an inline value not represented by a resource.
- out_entry->entry = overlay_entry.GetInlineValue();
- out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
- cookie = id_map.cookie;
+ result->entry = overlay_entry.GetInlineValue();
+ result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ result->cookie = id_map.cookie;
continue;
}
- FindEntryResult overlay_result;
- ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override,
- false /* stop_at_first_match */,
- ignore_configuration, &overlay_result);
- if (UNLIKELY(overlay_cookie == kInvalidCookie)) {
+ auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
+ false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (UNLIKELY(IsIOError(overlay_result))) {
+ return base::unexpected(overlay_result.error());
+ }
+ if (!overlay_result.has_value()) {
continue;
}
- if (!overlay_result.config.isBetterThan(out_entry->config, desired_config)
- && overlay_result.config.compare(out_entry->config) != 0) {
+ if (!overlay_result->config.isBetterThan(result->config, desired_config)
+ && overlay_result->config.compare(result->config) != 0) {
// The configuration of the entry for the overlay must be equal to or better than the target
// configuration to be chosen as the better value.
continue;
}
- cookie = overlay_cookie;
- out_entry->entry = overlay_result.entry;
- out_entry->config = overlay_result.config;
- out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
- if (resource_resolution_logging_enabled_) {
+ result->cookie = overlay_result->cookie;
+ result->entry = overlay_result->entry;
+ result->config = overlay_result->config;
+ result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+
+ if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(
- Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
- overlay_result.package_name});
+ Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(),
+ overlay_result->package_name});
}
}
}
- if (resource_resolution_logging_enabled_) {
- last_resolution_.cookie = cookie;
- last_resolution_.type_string_ref = out_entry->type_string_ref;
- last_resolution_.entry_string_ref = out_entry->entry_string_ref;
+ if (UNLIKELY(logging_enabled)) {
+ last_resolution_.cookie = result->cookie;
+ last_resolution_.type_string_ref = result->type_string_ref;
+ last_resolution_.entry_string_ref = result->entry_string_ref;
}
- return cookie;
+ return result;
}
-ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group,
- uint8_t type_idx, uint16_t entry_idx,
- const ResTable_config& desired_config,
- bool /*stop_at_first_match*/,
- bool ignore_configuration,
- FindEntryResult* out_entry) const {
+base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
+ const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx,
+ const ResTable_config& desired_config, bool stop_at_first_match,
+ bool ignore_configuration) const {
+ const bool logging_enabled = resource_resolution_logging_enabled_;
ApkAssetsCookie best_cookie = kInvalidCookie;
const LoadedPackage* best_package = nullptr;
- const ResTable_type* best_type = nullptr;
+ incfs::verified_map_ptr<ResTable_type> best_type;
const ResTable_config* best_config = nullptr;
ResTable_config best_config_copy;
- uint32_t best_offset = 0u;
- uint32_t type_flags = 0u;
+ uint32_t best_offset = 0U;
+ uint32_t type_flags = 0U;
- Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY;
+ auto resolution_type = Resolution::Step::Type::NO_ENTRY;
std::vector<Resolution::Step> resolution_steps;
// If desired_config is the same as the set configuration, then we can use our filtered list
@@ -630,17 +669,20 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro
continue;
}
+ auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx);
+ if (UNLIKELY(!entry_flags)) {
+ return base::unexpected(entry_flags.error());
+ }
+ type_flags |= entry_flags.value();
+
// If the package is an overlay or custom loader,
// then even configurations that are the same MUST be chosen.
const bool package_is_loader = loaded_package->IsCustomLoader();
- type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx);
if (use_fast_path) {
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
- const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
- const size_t type_count = candidate_configs.size();
- for (uint32_t i = 0; i < type_count; i++) {
- const ResTable_config& this_config = candidate_configs[i];
+ for (const auto& type_config : filtered_group.type_configs) {
+ const ResTable_config& this_config = type_config.config;
// We can skip calling ResTable_config::match() because we know that all candidate
// configurations that do NOT match have been filtered-out.
@@ -652,7 +694,7 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro
} else if (package_is_loader && this_config.compare(*best_config) == 0) {
resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
} else {
- if (resource_resolution_logging_enabled_) {
+ if (UNLIKELY(logging_enabled)) {
resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER
: Resolution::Step::Type::SKIPPED;
resolution_steps.push_back(Resolution::Step{resolution_type,
@@ -664,10 +706,13 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
- const ResTable_type* type = filtered_group.types[i];
- const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx);
- if (offset == ResTable_type::NO_ENTRY) {
- if (resource_resolution_logging_enabled_) {
+ const auto& type = type_config.type;
+ const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
+ if (UNLIKELY(IsIOError(offset))) {
+ return base::unexpected(offset.error());
+ }
+ if (!offset.has_value()) {
+ if (UNLIKELY(logging_enabled)) {
if (package_is_loader) {
resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER;
} else {
@@ -684,9 +729,9 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro
best_package = loaded_package;
best_type = type;
best_config = &this_config;
- best_offset = offset;
+ best_offset = offset.value();
- if (resource_resolution_logging_enabled_) {
+ if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
this_config.toString(),
&loaded_package->GetPackageName()});
@@ -700,10 +745,11 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro
// ResTable_config, we must copy it.
const auto iter_end = type_spec->types + type_spec->type_count;
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- ResTable_config this_config{};
+ const incfs::verified_map_ptr<ResTable_type>& type = *iter;
+ ResTable_config this_config{};
if (!ignore_configuration) {
- this_config.copyFromDtoH((*iter)->config);
+ this_config.copyFromDtoH(type->config);
if (!this_config.match(desired_config)) {
continue;
}
@@ -722,24 +768,27 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
- const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx);
- if (offset == ResTable_type::NO_ENTRY) {
+ const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx);
+ if (UNLIKELY(IsIOError(offset))) {
+ return base::unexpected(offset.error());
+ }
+ if (!offset.has_value()) {
continue;
}
best_cookie = cookie;
best_package = loaded_package;
- best_type = *iter;
+ best_type = type;
best_config_copy = this_config;
best_config = &best_config_copy;
- best_offset = offset;
+ best_offset = offset.value();
- if (ignore_configuration) {
+ if (stop_at_first_match) {
// Any configuration will suffice, so break.
break;
}
- if (resource_resolution_logging_enabled_) {
+ if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(Resolution::Step{resolution_type,
this_config.toString(),
&loaded_package->GetPackageName()});
@@ -749,36 +798,35 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro
}
if (UNLIKELY(best_cookie == kInvalidCookie)) {
- return kInvalidCookie;
+ return base::unexpected(std::nullopt);
}
- const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
- if (UNLIKELY(best_entry == nullptr)) {
- return kInvalidCookie;
+ auto best_entry_result = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
+ if (!best_entry_result.has_value()) {
+ return base::unexpected(best_entry_result.error());
}
- const uint16_t entry_size = dtohs(best_entry->size);
- if (entry_size >= sizeof(ResTable_map_entry) &&
- (dtohs(best_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
- // The entry represents a bag/map.
- out_entry->entry = reinterpret_cast<const ResTable_map_entry*>(best_entry);
- } else {
- // The entry represents a value.
- Res_value value;
- value.copyFrom_dtoh(*reinterpret_cast<const Res_value*>(
- reinterpret_cast<const uint8_t*>(best_entry) + entry_size));
- out_entry->entry = value;
- }
-
- out_entry->config = *best_config;
- out_entry->type_flags = type_flags;
- out_entry->package_name = &best_package->GetPackageName();
- out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
- out_entry->entry_string_ref =
- StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
- out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get();
-
- return best_cookie;
+ const incfs::map_ptr<ResTable_entry> best_entry = *best_entry_result;
+ if (!best_entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ const auto entry = GetEntryValue(best_entry.verified());
+ if (!entry.has_value()) {
+ return base::unexpected(entry.error());
+ }
+
+ return FindEntryResult{
+ .cookie = best_cookie,
+ .entry = *entry,
+ .config = *best_config,
+ .type_flags = type_flags,
+ .package_name = &best_package->GetPackageName(),
+ .type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1),
+ .entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(),
+ best_entry->key.index),
+ .dynamic_ref_table = package_group.dynamic_ref_table.get(),
+ };
}
void AssetManager2::ResetResourceResolution() const {
@@ -799,30 +847,28 @@ void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
std::string AssetManager2::GetLastResourceResolution() const {
if (!resource_resolution_logging_enabled_) {
LOG(ERROR) << "Must enable resource resolution logging before getting path.";
- return std::string();
+ return {};
}
auto cookie = last_resolution_.cookie;
if (cookie == kInvalidCookie) {
LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
- return std::string();
+ return {};
}
uint32_t resid = last_resolution_.resid;
std::vector<Resolution::Step>& steps = last_resolution_.steps;
-
- ResourceName resource_name;
std::string resource_name_string;
const LoadedPackage* package =
apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
if (package != nullptr) {
- ToResourceName(last_resolution_.type_string_ref,
- last_resolution_.entry_string_ref,
- package->GetPackageName(),
- &resource_name);
- resource_name_string = ToFormattedResourceString(&resource_name);
+ auto resource_name = ToResourceName(last_resolution_.type_string_ref,
+ last_resolution_.entry_string_ref,
+ package->GetPackageName());
+ resource_name_string = resource_name.has_value() ?
+ ToFormattedResourceString(resource_name.value()) : "<unknown>";
}
std::stringstream log_stream;
@@ -875,200 +921,206 @@ std::string AssetManager2::GetLastResourceResolution() const {
return log_stream.str();
}
-bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
- FindEntryResult entry;
- ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
- true /* stop_at_first_match */,
- true /* ignore_configuration */, &entry);
- if (cookie == kInvalidCookie) {
- return false;
+base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetResourceName(
+ uint32_t resid) const {
+ auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */,
+ true /* ignore_configuration */);
+ if (!result.has_value()) {
+ return base::unexpected(result.error());
}
- return ToResourceName(entry.type_string_ref,
- entry.entry_string_ref,
- *entry.package_name,
- out_name);
+ return ToResourceName(result->type_string_ref,
+ result->entry_string_ref,
+ *result->package_name);
}
-bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
- FindEntryResult entry;
- ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
- false /* stop_at_first_match */,
- true /* ignore_configuration */, &entry);
- if (cookie != kInvalidCookie) {
- *out_flags = entry.type_flags;
- return true;
+base::expected<AssetManager2::SelectedValue, NullOrIOError> AssetManager2::GetResource(
+ uint32_t resid, bool may_be_bag, uint16_t density_override) const {
+ auto result = FindEntry(resid, density_override, false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (!result.has_value()) {
+ return base::unexpected(result.error());
}
- return false;
-}
-ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
- uint16_t density_override, Res_value* out_value,
- ResTable_config* out_selected_config,
- uint32_t* out_flags) const {
- FindEntryResult entry;
- ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */,
- false /* ignore_configuration */, &entry);
- if (cookie == kInvalidCookie) {
- return kInvalidCookie;
- }
-
- auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
+ auto result_map_entry = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&result->entry);
if (result_map_entry != nullptr) {
if (!may_be_bag) {
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
- return kInvalidCookie;
+ return base::unexpected(std::nullopt);
}
// Create a reference since we can't represent this complex type as a Res_value.
- out_value->dataType = Res_value::TYPE_REFERENCE;
- out_value->data = resid;
- *out_selected_config = entry.config;
- *out_flags = entry.type_flags;
- return cookie;
+ return SelectedValue(Res_value::TYPE_REFERENCE, resid, result->cookie, result->type_flags,
+ resid, result->config);
}
// Convert the package ID to the runtime assigned package ID.
- *out_value = std::get<Res_value>(entry.entry);
- entry.dynamic_ref_table->lookupResourceValue(out_value);
+ Res_value value = std::get<Res_value>(result->entry);
+ result->dynamic_ref_table->lookupResourceValue(&value);
- *out_selected_config = entry.config;
- *out_flags = entry.type_flags;
- return cookie;
+ return SelectedValue(value.dataType, value.data, result->cookie, result->type_flags,
+ resid, result->config);
}
-ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
- ResTable_config* in_out_selected_config,
- uint32_t* in_out_flags,
- uint32_t* out_last_reference) const {
- constexpr const int kMaxIterations = 20;
-
- for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
- in_out_value->data != 0u && iteration < kMaxIterations;
- iteration++) {
- *out_last_reference = in_out_value->data;
- uint32_t new_flags = 0u;
- cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/,
- in_out_value, in_out_selected_config, &new_flags);
- if (cookie == kInvalidCookie) {
- return kInvalidCookie;
+base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference(
+ AssetManager2::SelectedValue& value, bool cache_value) const {
+ if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) {
+ // Not a reference. Nothing to do.
+ return {};
+ }
+
+ const uint32_t original_flags = value.flags;
+ const uint32_t original_resid = value.data;
+ if (cache_value) {
+ auto cached_value = cached_resolved_values_.find(value.data);
+ if (cached_value != cached_resolved_values_.end()) {
+ value = cached_value->second;
+ value.flags |= original_flags;
+ return {};
}
- if (in_out_flags != nullptr) {
- *in_out_flags |= new_flags;
+ }
+
+ uint32_t combined_flags = 0U;
+ uint32_t resolve_resid = original_resid;
+ constexpr const uint32_t kMaxIterations = 20;
+ for (uint32_t i = 0U;; i++) {
+ auto result = GetResource(resolve_resid, true /*may_be_bag*/);
+ if (!result.has_value()) {
+ value.resid = resolve_resid;
+ return base::unexpected(result.error());
}
- if (*out_last_reference == in_out_value->data) {
+
+ // If resource resolution fails, the value should be set to the last reference that was able to
+ // be resolved successfully.
+ value = *result;
+ value.flags |= combined_flags;
+
+ if (result->type != Res_value::TYPE_REFERENCE ||
+ result->data == Res_value::DATA_NULL_UNDEFINED ||
+ result->data == resolve_resid || i == kMaxIterations) {
// This reference can't be resolved, so exit now and let the caller deal with it.
- return cookie;
+ if (cache_value) {
+ cached_resolved_values_[original_resid] = value;
+ }
+
+ // Above value is cached without original_flags to ensure they don't get included in future
+ // queries that hit the cache
+ value.flags |= original_flags;
+ return {};
}
+
+ combined_flags = result->flags;
+ resolve_resid = result->data;
}
- return cookie;
}
-const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) {
+const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) const {
auto cached_iter = cached_bag_resid_stacks_.find(resid);
if (cached_iter != cached_bag_resid_stacks_.end()) {
return cached_iter->second;
- } else {
- auto found_resids = std::vector<uint32_t>();
- GetBag(resid, found_resids);
- // Cache style stacks if they are not already cached.
- cached_bag_resid_stacks_[resid] = found_resids;
- return found_resids;
}
+
+ std::vector<uint32_t> found_resids;
+ GetBag(resid, found_resids);
+ cached_bag_resid_stacks_.emplace(resid, found_resids);
+ return found_resids;
}
-const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
- auto found_resids = std::vector<uint32_t>();
- auto bag = GetBag(resid, found_resids);
+base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag(
+ AssetManager2::SelectedValue& value) const {
+ if (UNLIKELY(value.type != Res_value::TYPE_REFERENCE)) {
+ return base::unexpected(std::nullopt);
+ }
- // Cache style stacks if they are not already cached.
- auto cached_iter = cached_bag_resid_stacks_.find(resid);
- if (cached_iter == cached_bag_resid_stacks_.end()) {
- cached_bag_resid_stacks_[resid] = found_resids;
+ auto bag = GetBag(value.data);
+ if (bag.has_value()) {
+ value.flags |= (*bag)->type_spec_flags;
}
return bag;
}
-static bool compare_bag_entries(const ResolvedBag::Entry& entry1,
- const ResolvedBag::Entry& entry2) {
- return entry1.key < entry2.key;
+base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const {
+ std::vector<uint32_t> found_resids;
+ return GetBag(resid, found_resids);
}
-const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) {
- auto cached_iter = cached_bags_.find(resid);
- if (cached_iter != cached_bags_.end()) {
+base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(
+ uint32_t resid, std::vector<uint32_t>& child_resids) const {
+ if (auto cached_iter = cached_bags_.find(resid); cached_iter != cached_bags_.end()) {
return cached_iter->second.get();
}
- FindEntryResult entry;
- ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
- false /* stop_at_first_match */,
- false /* ignore_configuration */,
- &entry);
- if (cookie == kInvalidCookie) {
- return nullptr;
+ 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 result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
- if (result_map_entry == nullptr) {
+ 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 nullptr;
+ return base::unexpected(std::nullopt);
}
- auto map = reinterpret_cast<const ResTable_map_entry*>(*result_map_entry);
- auto map_entry = reinterpret_cast<const ResTable_map*>(
- reinterpret_cast<const uint8_t*>(map) + map->size);
- const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
+ auto map = *entry_map;
+ auto map_entry = map.offset(dtohs(map->size)).convert<ResTable_map>();
+ const auto map_entry_end = map_entry + dtohl(map->count);
// Keep track of ids that have already been seen to prevent infinite loops caused by circular
- // dependencies between bags
+ // dependencies between bags.
child_resids.push_back(resid);
uint32_t parent_resid = dtohl(map->parent.ident);
- if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid)
- != child_resids.end()) {
- // There is no parent or a circular dependency exist, meaning there is nothing to inherit and
- // we can do a simple copy of the entries in the map.
+ if (parent_resid == 0U ||
+ std::find(child_resids.begin(), child_resids.end(), parent_resid) != child_resids.end()) {
+ // There is no parent or a circular parental dependency exist, meaning there is nothing to
+ // inherit and we can do a simple copy of the entries in the map.
const size_t entry_count = map_entry_end - map_entry;
util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
bool sort_entries = false;
- ResolvedBag::Entry* new_entry = new_bag->entries;
- for (; map_entry != map_entry_end; ++map_entry) {
+ for (auto new_entry = new_bag->entries; map_entry != map_entry_end; ++map_entry) {
+ if (UNLIKELY(!map_entry)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
uint32_t new_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(new_key)) {
// Attributes, arrays, etc don't have a resource id as the name. They specify
// other data, which would be wrong to change via a lookup.
- if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
+ if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
resid);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
}
- new_entry->cookie = cookie;
+
+ new_entry->cookie = entry->cookie;
new_entry->key = new_key;
new_entry->key_pool = nullptr;
new_entry->type_pool = nullptr;
new_entry->style = resid;
new_entry->value.copyFrom_dtoh(map_entry->value);
- status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value);
- if (err != NO_ERROR) {
+ status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
+ if (UNLIKELY(err != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf(
"Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType,
new_entry->value.data, new_key);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
+
sort_entries = sort_entries ||
(new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
++new_entry;
}
if (sort_entries) {
- std::sort(new_bag->entries, new_bag->entries + entry_count, compare_bag_entries);
+ std::sort(new_bag->entries, new_bag->entries + entry_count,
+ [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; });
}
- new_bag->type_spec_flags = entry.type_flags;
+ new_bag->type_spec_flags = entry->type_flags;
new_bag->entry_count = static_cast<uint32_t>(entry_count);
ResolvedBag* result = new_bag.get();
cached_bags_[resid] = std::move(new_bag);
@@ -1076,54 +1128,58 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
}
// In case the parent is a dynamic reference, resolve it.
- entry.dynamic_ref_table->lookupResourceId(&parent_resid);
+ entry->dynamic_ref_table->lookupResourceId(&parent_resid);
// Get the parent and do a merge of the keys.
- const ResolvedBag* parent_bag = GetBag(parent_resid, child_resids);
- if (parent_bag == nullptr) {
+ const auto parent_bag = GetBag(parent_resid, child_resids);
+ if (UNLIKELY(!parent_bag.has_value())) {
// Failed to get the parent that should exist.
LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid,
resid);
- return nullptr;
+ return base::unexpected(parent_bag.error());
}
// Create the max possible entries we can make. Once we construct the bag,
// we will realloc to fit to size.
- const size_t max_count = parent_bag->entry_count + dtohl(map->count);
+ const size_t max_count = (*parent_bag)->entry_count + dtohl(map->count);
util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))};
ResolvedBag::Entry* new_entry = new_bag->entries;
- const ResolvedBag::Entry* parent_entry = parent_bag->entries;
- const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
+ const ResolvedBag::Entry* parent_entry = (*parent_bag)->entries;
+ const ResolvedBag::Entry* const parent_entry_end = parent_entry + (*parent_bag)->entry_count;
// The keys are expected to be in sorted order. Merge the two bags.
bool sort_entries = false;
while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
+ if (UNLIKELY(!map_entry)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
uint32_t child_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(child_key)) {
- if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) {
+ if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key,
resid);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
}
if (child_key <= parent_entry->key) {
// Use the child key if it comes before the parent
// or is equal to the parent (overrides).
- new_entry->cookie = cookie;
+ new_entry->cookie = entry->cookie;
new_entry->key = child_key;
new_entry->key_pool = nullptr;
new_entry->type_pool = nullptr;
new_entry->value.copyFrom_dtoh(map_entry->value);
new_entry->style = resid;
- status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value);
- if (err != NO_ERROR) {
+ status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
+ if (UNLIKELY(err != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf(
"Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType,
new_entry->value.data, child_key);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
++map_entry;
} else {
@@ -1143,25 +1199,29 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
// Finish the child entries if they exist.
while (map_entry != map_entry_end) {
+ if (UNLIKELY(!map_entry)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
uint32_t new_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(new_key)) {
- if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
+ if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
resid);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
}
- new_entry->cookie = cookie;
+ new_entry->cookie = entry->cookie;
new_entry->key = new_key;
new_entry->key_pool = nullptr;
new_entry->type_pool = nullptr;
new_entry->value.copyFrom_dtoh(map_entry->value);
new_entry->style = resid;
- status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value);
- if (err != NO_ERROR) {
+ status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value);
+ if (UNLIKELY(err != NO_ERROR)) {
LOG(ERROR) << base::StringPrintf("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.",
new_entry->value.dataType, new_entry->value.data, new_key);
- return nullptr;
+ return base::unexpected(std::nullopt);
}
sort_entries = sort_entries ||
(new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
@@ -1185,11 +1245,12 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
}
if (sort_entries) {
- std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries);
+ std::sort(new_bag->entries, new_bag->entries + actual_count,
+ [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; });
}
// Combine flags from the parent and our own bag.
- new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags;
+ new_bag->type_spec_flags = entry->type_flags | (*parent_bag)->type_spec_flags;
new_bag->entry_count = static_cast<uint32_t>(actual_count);
ResolvedBag* result = new_bag.get();
cached_bags_[resid] = std::move(new_bag);
@@ -1208,16 +1269,16 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) {
return true;
}
-uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
- const std::string& fallback_type,
- const std::string& fallback_package) const {
+base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId(
+ const std::string& resource_name, const std::string& fallback_type,
+ const std::string& fallback_package) const {
StringPiece package_name, type, entry;
if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) {
- return 0u;
+ return base::unexpected(std::nullopt);
}
if (entry.empty()) {
- return 0u;
+ return base::unexpected(std::nullopt);
}
if (package_name.empty()) {
@@ -1230,12 +1291,12 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
std::u16string type16;
if (!Utf8ToUtf16(type, &type16)) {
- return 0u;
+ return base::unexpected(std::nullopt);
}
std::u16string entry16;
if (!Utf8ToUtf16(entry, &entry16)) {
- return 0u;
+ return base::unexpected(std::nullopt);
}
const StringPiece16 kAttr16 = u"attr";
@@ -1249,20 +1310,24 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
break;
}
- uint32_t resid = package->FindEntryByName(type16, entry16);
- if (resid == 0u && kAttr16 == type16) {
+ base::expected<uint32_t, NullOrIOError> resid = package->FindEntryByName(type16, entry16);
+ if (UNLIKELY(IsIOError(resid))) {
+ return base::unexpected(resid.error());
+ }
+
+ if (!resid.has_value() && kAttr16 == type16) {
// Private attributes in libraries (such as the framework) are sometimes encoded
// under the type '^attr-private' in order to leave the ID space of public 'attr'
// free for future additions. Check '^attr-private' for the same name.
resid = package->FindEntryByName(kAttrPrivate16, entry16);
}
- if (resid != 0u) {
- return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId);
+ if (resid.has_value()) {
+ return fix_package_id(*resid, package_group.dynamic_ref_table->mAssignedPackageId);
}
}
}
- return 0u;
+ return base::unexpected(std::nullopt);
}
void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) {
@@ -1282,8 +1347,7 @@ void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) {
ResTable_config this_config;
this_config.copyFromDtoH((*iter)->config);
if (!filter_incompatible_configs || this_config.match(configuration_)) {
- group.configurations.push_back(this_config);
- group.types.push_back(*iter);
+ group.type_configs.push_back(TypeConfig{*iter, this_config});
}
}
});
@@ -1309,6 +1373,8 @@ void AssetManager2::InvalidateCaches(uint32_t diff) {
++iter;
}
}
+
+ cached_resolved_values_.clear();
}
uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const {
@@ -1354,16 +1420,16 @@ struct Theme::Package {
std::array<util::unique_cptr<ThemeType>, kTypeCount> types;
};
-bool Theme::ApplyStyle(uint32_t resid, bool force) {
+base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, bool force) {
ATRACE_NAME("Theme::ApplyStyle");
- const ResolvedBag* bag = asset_manager_->GetBag(resid);
- if (bag == nullptr) {
- return false;
+ auto bag = asset_manager_->GetBag(resid);
+ if (!bag.has_value()) {
+ return base::unexpected(bag.error());
}
// Merge the flags from this style.
- type_spec_flags_ |= bag->type_spec_flags;
+ type_spec_flags_ |= (*bag)->type_spec_flags;
int last_type_idx = -1;
int last_package_idx = -1;
@@ -1373,14 +1439,14 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) {
// Iterate backwards, because each bag is sorted in ascending key ID order, meaning we will only
// need to perform one resize per type.
using reverse_bag_iterator = std::reverse_iterator<const ResolvedBag::Entry*>;
- const auto bag_iter_end = reverse_bag_iterator(begin(bag));
- for (auto bag_iter = reverse_bag_iterator(end(bag)); bag_iter != bag_iter_end; ++bag_iter) {
- const uint32_t attr_resid = bag_iter->key;
+ const auto rbegin = reverse_bag_iterator(begin(*bag));
+ for (auto it = reverse_bag_iterator(end(*bag)); it != rbegin; ++it) {
+ const uint32_t attr_resid = it->key;
// If the resource ID passed in is not a style, the key can be some other identifier that is not
// a resource ID. We should fail fast instead of operating with strange resource IDs.
if (!is_valid_resid(attr_resid)) {
- return false;
+ return base::unexpected(std::nullopt);
}
// We don't use the 0-based index for the type so that we can avoid doing ID validation
@@ -1428,20 +1494,18 @@ bool Theme::ApplyStyle(uint32_t resid, bool force) {
ThemeEntry& entry = last_type->entries[entry_idx];
if (force || (entry.value.dataType == Res_value::TYPE_NULL &&
entry.value.data != Res_value::DATA_NULL_EMPTY)) {
- entry.cookie = bag_iter->cookie;
- entry.type_spec_flags |= bag->type_spec_flags;
- entry.value = bag_iter->value;
+ entry.cookie = it->cookie;
+ entry.type_spec_flags |= (*bag)->type_spec_flags;
+ entry.value = it->value;
}
}
- return true;
+ return {};
}
-ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
- uint32_t* out_flags) const {
- int cnt = 20;
+std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) const {
+ int cnt = 20;
uint32_t type_spec_flags = 0u;
-
do {
const int package_idx = get_package_id(resid);
const Package* package = packages_[package_idx].get();
@@ -1461,43 +1525,42 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
resid = entry.value.data;
continue;
}
- return kInvalidCookie;
+ return std::nullopt;
}
// @null is different than @empty.
if (entry.value.dataType == Res_value::TYPE_NULL &&
entry.value.data != Res_value::DATA_NULL_EMPTY) {
- return kInvalidCookie;
+ return std::nullopt;
}
- *out_value = entry.value;
- *out_flags = type_spec_flags;
- return entry.cookie;
+ return AssetManager2::SelectedValue(entry.value.dataType, entry.value.data, entry.cookie,
+ type_spec_flags, 0U /* resid */, {} /* config */);
}
}
}
break;
} while (true);
- return kInvalidCookie;
+ return std::nullopt;
}
-ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
- ResTable_config* in_out_selected_config,
- uint32_t* in_out_type_spec_flags,
- uint32_t* out_last_ref) const {
- if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) {
- uint32_t new_flags;
- cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags);
- if (cookie == kInvalidCookie) {
- return kInvalidCookie;
- }
+base::expected<std::monostate, NullOrIOError> Theme::ResolveAttributeReference(
+ AssetManager2::SelectedValue& value) const {
+ if (value.type != Res_value::TYPE_ATTRIBUTE) {
+ return asset_manager_->ResolveReference(value);
+ }
- if (in_out_type_spec_flags != nullptr) {
- *in_out_type_spec_flags |= new_flags;
- }
+ std::optional<AssetManager2::SelectedValue> result = GetAttribute(value.data);
+ if (!result.has_value()) {
+ return base::unexpected(std::nullopt);
}
- return asset_manager_->ResolveReference(cookie, in_out_value, in_out_selected_config,
- in_out_type_spec_flags, out_last_ref);
+
+ auto resolve_result = asset_manager_->ResolveReference(*result, true /* cache_value */);
+ if (resolve_result.has_value()) {
+ result->flags |= value.flags;
+ value = *result;
+ }
+ return resolve_result;
}
void Theme::Clear() {
@@ -1507,9 +1570,9 @@ void Theme::Clear() {
}
}
-void Theme::SetTo(const Theme& o) {
+base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) {
if (this == &o) {
- return;
+ return {};
}
type_spec_flags_ = o.type_spec_flags_;
@@ -1560,10 +1623,8 @@ void Theme::SetTo(const Theme& o) {
// Map the runtime package of the source apk asset to the destination apk asset.
if (src_asset->GetPath() == dest_asset->GetPath()) {
- const std::vector<std::unique_ptr<const LoadedPackage>>& src_packages =
- src_asset->GetLoadedArsc()->GetPackages();
- const std::vector<std::unique_ptr<const LoadedPackage>>& dest_packages =
- dest_asset->GetLoadedArsc()->GetPackages();
+ const auto& src_packages = src_asset->GetLoadedArsc()->GetPackages();
+ const auto& dest_packages = dest_asset->GetLoadedArsc()->GetPackages();
SourceToDestinationRuntimePackageMap package_map;
@@ -1660,15 +1721,20 @@ void Theme::SetTo(const Theme& o) {
int attribute_dest_package_id = p;
if (attribute_dest_package_id != 0x01) {
// Find the cookie of the attribute resource id in the source AssetManager
- FindEntryResult attribute_entry_result;
- ApkAssetsCookie attribute_cookie =
+ base::expected<FindEntryResult, NullOrIOError> attribute_entry_result =
o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ ,
true /* stop_at_first_match */,
- true /* ignore_configuration */,
- &attribute_entry_result);
+ true /* ignore_configuration */);
+ if (UNLIKELY(IsIOError(attribute_entry_result))) {
+ return base::unexpected(GetIOError(attribute_entry_result.error()));
+ }
+ if (!attribute_entry_result.has_value()) {
+ continue;
+ }
// Determine the package id of the attribute in the destination AssetManager.
- auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie);
+ auto attribute_package_map = src_asset_cookie_id_map.find(
+ attribute_entry_result->cookie);
if (attribute_package_map == src_asset_cookie_id_map.end()) {
continue;
}
@@ -1712,6 +1778,7 @@ void Theme::SetTo(const Theme& o) {
}
}
}
+ return {};
}
void Theme::Dump() const {
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index e62fb614e195..c188712e5877 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -24,9 +24,12 @@
#include "androidfw/AttributeFinder.h"
constexpr bool kDebugStyles = false;
+#define DEBUG_LOG(...) do { if (kDebugStyles) { ALOGI(__VA_ARGS__); } } while(0)
namespace android {
+namespace {
+
// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1);
@@ -36,8 +39,7 @@ class XmlAttributeFinder
: public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
public:
explicit XmlAttributeFinder(const ResXMLParser* parser)
- : BackTrackingAttributeFinder(
- 0, parser != nullptr ? parser->getAttributeCount() : 0),
+ : BackTrackingAttributeFinder(0, parser != nullptr ? parser->getAttributeCount() : 0),
parser_(parser) {}
inline uint32_t GetAttribute(size_t index) const {
@@ -61,136 +63,149 @@ class BagAttributeFinder
}
};
-bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
- uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
- if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme,
- def_style_attr, def_style_res);
+base::expected<const ResolvedBag*, NullOrIOError> GetStyleBag(Theme* theme,
+ uint32_t theme_attribute_resid,
+ uint32_t fallback_resid,
+ uint32_t* out_theme_flags) {
+ // Load the style from the attribute if specified.
+ if (theme_attribute_resid != 0U) {
+ std::optional<AssetManager2::SelectedValue> value = theme->GetAttribute(theme_attribute_resid);
+ if (value.has_value()) {
+ *out_theme_flags |= value->flags;
+ auto result = theme->GetAssetManager()->ResolveBag(*value);
+ if (result.has_value() || IsIOError(result)) {
+ return result;
+ }
+ }
}
- AssetManager2* assetmanager = theme->GetAssetManager();
- ResTable_config config;
- Res_value value;
+ // Fallback to loading the style from the resource id if specified.
+ if (fallback_resid != 0U) {
+ return theme->GetAssetManager()->GetBag(fallback_resid);
+ }
- int indices_idx = 0;
+ return base::unexpected(std::nullopt);
+}
- // Load default style from attribute, if specified...
- uint32_t def_style_flags = 0u;
- if (def_style_attr != 0) {
- Res_value value;
- if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- def_style_res = value.data;
- }
- }
+base::expected<const ResolvedBag*, NullOrIOError> GetXmlStyleBag(Theme* theme,
+ ResXMLParser* xml_parser,
+ uint32_t* out_theme_flags) {
+ if (xml_parser == nullptr) {
+ return base::unexpected(std::nullopt);
}
- // Retrieve the default style bag, if requested.
- const ResolvedBag* default_style_bag = nullptr;
- if (def_style_res != 0) {
- default_style_bag = assetmanager->GetBag(def_style_res);
- if (default_style_bag != nullptr) {
- def_style_flags |= default_style_bag->type_spec_flags;
+ // Retrieve the style resource ID associated with the current XML tag's style attribute.
+ Res_value value;
+ const ssize_t idx = xml_parser->indexOfStyle();
+ if (idx < 0 || xml_parser->getAttributeValue(idx, &value) < 0) {
+ return base::unexpected(std::nullopt);
+ }
+
+ if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
+ // Resolve the attribute with out theme.
+ if (std::optional<AssetManager2::SelectedValue> result = theme->GetAttribute(value.data)) {
+ *out_theme_flags |= result->flags;
+ return theme->GetAssetManager()->ResolveBag(*result);
}
}
- BagAttributeFinder def_style_attr_finder(default_style_bag);
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ return theme->GetAssetManager()->GetBag(value.data);
+ }
+
+ return base::unexpected(std::nullopt);
+}
+
+} // namespace
+
+base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_style_attr,
+ uint32_t def_style_res, uint32_t* src_values,
+ size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values,
+ uint32_t* out_indices) {
+ DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr,
+ def_style_res);
+
+ int indices_idx = 0;
+ const AssetManager2* assetmanager = theme->GetAssetManager();
+
+ // Load default style from attribute or resource id, if specified...
+ uint32_t def_style_theme_flags = 0U;
+ const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_res,
+ &def_style_theme_flags);
+ if (UNLIKELY(IsIOError(default_style_bag))) {
+ return base::unexpected(GetIOError(default_style_bag.error()));
+ }
+
+ BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr));
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
-
- if (kDebugStyles) {
- ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
- }
-
- ApkAssetsCookie cookie = kInvalidCookie;
- uint32_t type_set_flags = 0;
-
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- config.density = 0;
+ DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident);
// Try to find a value for this attribute... we prioritize values
// coming from, first XML attributes, then XML style, then default
// style, and finally the theme.
// Retrieve the current input value if available.
+ AssetManager2::SelectedValue value{};
if (src_values_length > 0 && src_values[ii] != 0) {
- value.dataType = Res_value::TYPE_ATTRIBUTE;
+ value.type = Res_value::TYPE_ATTRIBUTE;
value.data = src_values[ii];
- if (kDebugStyles) {
- ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
+ DEBUG_LOG("-> From values: type=0x%x, data=0x%08x", value.type, value.data);
} else {
const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident);
if (entry != def_style_attr_finder.end()) {
- cookie = entry->cookie;
- type_set_flags = def_style_flags;
- value = entry->value;
- if (kDebugStyles) {
- ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
+ value = AssetManager2::SelectedValue(*default_style_bag, *entry);
+ value.flags |= def_style_theme_flags;
+ DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x", value.type, value.data);
}
}
- uint32_t resid = 0;
- if (value.dataType != Res_value::TYPE_NULL) {
+ if (value.type != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ApkAssetsCookie new_cookie =
- theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
- }
- if (kDebugStyles) {
- ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
+ const auto result = theme->ResolveAttributeReference(value);
+ if (UNLIKELY(IsIOError(result))) {
+ return base::unexpected(GetIOError(result.error()));
}
+ DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data);
} else if (value.data != Res_value::DATA_NULL_EMPTY) {
// If we still don't have a value for this attribute, try to find it in the theme!
- ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
- if (new_cookie != kInvalidCookie) {
- if (kDebugStyles) {
- ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- new_cookie =
- assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
- }
- if (kDebugStyles) {
- ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
+ if (auto attr_value = theme->GetAttribute(cur_ident)) {
+ value = *attr_value;
+ DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data);
+
+ const auto result = assetmanager->ResolveReference(value, true /* cache_value */);
+ if (UNLIKELY(IsIOError(result))) {
+ return base::unexpected(GetIOError(result.error()));
}
+ DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data);
}
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- if (kDebugStyles) {
- ALOGI("-> Setting to @null!");
- }
- value.dataType = Res_value::TYPE_NULL;
+ if (value.type == Res_value::TYPE_REFERENCE && value.data == 0) {
+ DEBUG_LOG("-> Setting to @null!");
+ value.type = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- cookie = kInvalidCookie;
+ value.cookie = kInvalidCookie;
}
- if (kDebugStyles) {
- ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
- }
+ DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data);
// Write the final value back to Java.
- out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_TYPE] = value.type;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
- out_values[STYLE_RESOURCE_ID] = resid;
- out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
- out_values[STYLE_DENSITY] = config.density;
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie);
+ out_values[STYLE_RESOURCE_ID] = value.resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags;
+ out_values[STYLE_DENSITY] = value.config.density;
if (out_indices != nullptr &&
- (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
- indices_idx++;
- out_indices[indices_idx] = ii;
+ (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
+ out_indices[++indices_idx] = ii;
}
out_values += STYLE_NUM_ENTRIES;
@@ -199,93 +214,46 @@ bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}
- return true;
+ return {};
}
-void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
- uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
- uint32_t* out_values, uint32_t* out_indices) {
- if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
- def_style_attr, def_style_resid, xml_parser);
- }
-
- AssetManager2* assetmanager = theme->GetAssetManager();
- ResTable_config config;
- Res_value value;
+base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* xml_parser,
+ uint32_t def_style_attr,
+ uint32_t def_style_resid,
+ const uint32_t* attrs, size_t attrs_length,
+ uint32_t* out_values, uint32_t* out_indices) {
+ DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
+ def_style_attr, def_style_resid, xml_parser);
int indices_idx = 0;
+ const AssetManager2* assetmanager = theme->GetAssetManager();
// Load default style from attribute, if specified...
- uint32_t def_style_flags = 0u;
- if (def_style_attr != 0) {
- Res_value value;
- if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- def_style_resid = value.data;
- }
- }
+ uint32_t def_style_theme_flags = 0U;
+ const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_resid,
+ &def_style_theme_flags);
+ if (IsIOError(default_style_bag)) {
+ return base::unexpected(GetIOError(default_style_bag.error()));
}
// Retrieve the style resource ID associated with the current XML tag's style attribute.
- uint32_t style_resid = 0u;
- uint32_t style_flags = 0u;
- if (xml_parser != nullptr) {
- ssize_t idx = xml_parser->indexOfStyle();
- if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
- if (value.dataType == value.TYPE_ATTRIBUTE) {
- // Resolve the attribute with out theme.
- if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) {
- value.dataType = Res_value::TYPE_NULL;
- }
- }
-
- if (value.dataType == value.TYPE_REFERENCE) {
- style_resid = value.data;
- }
- }
- }
-
- // Retrieve the default style bag, if requested.
- const ResolvedBag* default_style_bag = nullptr;
- if (def_style_resid != 0) {
- default_style_bag = assetmanager->GetBag(def_style_resid);
- if (default_style_bag != nullptr) {
- def_style_flags |= default_style_bag->type_spec_flags;
- }
+ uint32_t xml_style_theme_flags = 0U;
+ const auto xml_style_bag = GetXmlStyleBag(theme, xml_parser, &def_style_theme_flags);
+ if (IsIOError(xml_style_bag)) {
+ return base::unexpected(GetIOError(xml_style_bag.error()));
}
- BagAttributeFinder def_style_attr_finder(default_style_bag);
-
- // Retrieve the style class bag, if requested.
- const ResolvedBag* xml_style_bag = nullptr;
- if (style_resid != 0) {
- xml_style_bag = assetmanager->GetBag(style_resid);
- if (xml_style_bag != nullptr) {
- style_flags |= xml_style_bag->type_spec_flags;
- }
- }
-
- BagAttributeFinder xml_style_attr_finder(xml_style_bag);
-
- // Retrieve the XML attributes, if requested.
+ BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr));
+ BagAttributeFinder xml_style_attr_finder(xml_style_bag.value_or(nullptr));
XmlAttributeFinder xml_attr_finder(xml_parser);
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
+ DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident);
- if (kDebugStyles) {
- ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
- }
-
- ApkAssetsCookie cookie = kInvalidCookie;
- uint32_t type_set_flags = 0u;
-
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- config.density = 0;
+ AssetManager2::SelectedValue value{};
uint32_t value_source_resid = 0;
// Try to find a value for this attribute... we prioritize values
@@ -296,178 +264,152 @@ void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
if (xml_attr_idx != xml_attr_finder.end()) {
// We found the attribute we were looking for.
- xml_parser->getAttributeValue(xml_attr_idx, &value);
- if (kDebugStyles) {
- ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
+ Res_value attribute_value{};
+ xml_parser->getAttributeValue(xml_attr_idx, &attribute_value);
+ value.type = attribute_value.dataType;
+ value.data = attribute_value.data;
value_source_resid = xml_parser->getSourceResourceId();
+ DEBUG_LOG("-> From XML: type=0x%x, data=0x%08x", value.type, value.data);
}
- if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
+ if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
// Walk through the style class values looking for the requested attribute.
const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident);
if (entry != xml_style_attr_finder.end()) {
- // We found the attribute we were looking for.
- cookie = entry->cookie;
- type_set_flags = style_flags;
- value = entry->value;
+ value = AssetManager2::SelectedValue(*xml_style_bag, *entry);
+ value.flags |= xml_style_theme_flags;
value_source_resid = entry->style;
- if (kDebugStyles) {
- ALOGI("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data,
- entry->style);
- }
+ DEBUG_LOG("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data,
+ value_source_resid);
}
}
- if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
+ if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
// Walk through the default style values looking for the requested attribute.
const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident);
if (entry != def_style_attr_finder.end()) {
- // We found the attribute we were looking for.
- cookie = entry->cookie;
- type_set_flags = def_style_flags;
- value = entry->value;
- if (kDebugStyles) {
- ALOGI("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data,
- entry->style);
- }
+ value = AssetManager2::SelectedValue(*default_style_bag, *entry);
+ value.flags |= def_style_theme_flags;
value_source_resid = entry->style;
+ DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data,
+ entry->style);
}
}
- uint32_t resid = 0u;
- if (value.dataType != Res_value::TYPE_NULL) {
+ if (value.type != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ApkAssetsCookie new_cookie =
- theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
- }
-
- if (kDebugStyles) {
- ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
+ auto result = theme->ResolveAttributeReference(value);
+ if (UNLIKELY(IsIOError(result))) {
+ return base::unexpected(GetIOError(result.error()));
}
+ DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data);
} else if (value.data != Res_value::DATA_NULL_EMPTY) {
// If we still don't have a value for this attribute, try to find it in the theme!
- ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
- // TODO: set value_source_resid for the style in the theme that was used.
- if (new_cookie != kInvalidCookie) {
- if (kDebugStyles) {
- ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- new_cookie =
- assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
- }
+ if (auto attr_value = theme->GetAttribute(cur_ident)) {
+ value = *attr_value;
+ DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data);
- if (kDebugStyles) {
- ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
+ auto result = assetmanager->ResolveReference(value, true /* cache_value */);
+ if (UNLIKELY(IsIOError(result))) {
+ return base::unexpected(GetIOError(result.error()));
}
+ DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data);
+ // TODO: set value_source_resid for the style in the theme that was used.
}
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- if (kDebugStyles) {
- ALOGI("-> Setting to @null!");
- }
- value.dataType = Res_value::TYPE_NULL;
+ if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) {
+ DEBUG_LOG("-> Setting to @null!");
+ value.type = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- cookie = kInvalidCookie;
+ value.cookie = kInvalidCookie;
}
- if (kDebugStyles) {
- ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
- }
+ DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data);
// Write the final value back to Java.
- out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_TYPE] = value.type;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
- out_values[STYLE_RESOURCE_ID] = resid;
- out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
- out_values[STYLE_DENSITY] = config.density;
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie);
+ out_values[STYLE_RESOURCE_ID] = value.resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags;
+ out_values[STYLE_DENSITY] = value.config.density;
out_values[STYLE_SOURCE_RESOURCE_ID] = value_source_resid;
- if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
- indices_idx++;
-
+ if (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
// out_indices must NOT be nullptr.
- out_indices[indices_idx] = ii;
+ out_indices[++indices_idx] = ii;
}
out_values += STYLE_NUM_ENTRIES;
}
// out_indices must NOT be nullptr.
out_indices[0] = indices_idx;
+ return {};
}
-bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
- ResTable_config config;
- Res_value value;
-
+base::expected<std::monostate, IOError> RetrieveAttributes(AssetManager2* assetmanager,
+ ResXMLParser* xml_parser,
+ uint32_t* attrs,
+ size_t attrs_length,
+ uint32_t* out_values,
+ uint32_t* out_indices) {
int indices_idx = 0;
// Retrieve the XML attributes, if requested.
- const size_t xml_attr_count = xml_parser->getAttributeCount();
size_t ix = 0;
+ const size_t xml_attr_count = xml_parser->getAttributeCount();
uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
- ApkAssetsCookie cookie = kInvalidCookie;
- uint32_t type_set_flags = 0u;
-
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- config.density = 0;
+ AssetManager2::SelectedValue value{};
// Try to find a value for this attribute...
// Skip through XML attributes until the end or the next possible match.
while (ix < xml_attr_count && cur_ident > cur_xml_attr) {
- ix++;
- cur_xml_attr = xml_parser->getAttributeNameResID(ix);
+ cur_xml_attr = xml_parser->getAttributeNameResID(++ix);
}
+
// Retrieve the current XML attribute if it matches, and step to next.
if (ix < xml_attr_count && cur_ident == cur_xml_attr) {
- xml_parser->getAttributeValue(ix, &value);
- ix++;
- cur_xml_attr = xml_parser->getAttributeNameResID(ix);
+ Res_value attribute_value{};
+ xml_parser->getAttributeValue(ix, &attribute_value);
+ value.type = attribute_value.dataType;
+ value.data = attribute_value.data;
+ cur_xml_attr = xml_parser->getAttributeNameResID(++ix);
}
- uint32_t resid = 0u;
- if (value.dataType != Res_value::TYPE_NULL) {
+ if (value.type != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ApkAssetsCookie new_cookie =
- assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
+ auto result = assetmanager->ResolveReference(value);
+ if (UNLIKELY(IsIOError(result))) {
+ return base::unexpected(GetIOError(result.error()));
}
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- value.dataType = Res_value::TYPE_NULL;
+ if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) {
+ value.type = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- cookie = kInvalidCookie;
+ value.cookie = kInvalidCookie;
}
// Write the final value back to Java.
- out_values[STYLE_TYPE] = value.dataType;
+ out_values[STYLE_TYPE] = value.type;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
- out_values[STYLE_RESOURCE_ID] = resid;
- out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
- out_values[STYLE_DENSITY] = config.density;
+ out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie);
+ out_values[STYLE_RESOURCE_ID] = value.resid;
+ out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags;
+ out_values[STYLE_DENSITY] = value.config.density;
if (out_indices != nullptr &&
- (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
- indices_idx++;
- out_indices[indices_idx] = ii;
+ (value.type != Res_value::TYPE_NULL ||
+ value.data == Res_value::DATA_NULL_EMPTY)) {
+ out_indices[++indices_idx] = ii;
}
out_values += STYLE_NUM_ENTRIES;
@@ -476,7 +418,7 @@ bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, u
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}
- return true;
+ return {};
}
} // namespace android
diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp
index 8fc321968055..25c8aa64e492 100644
--- a/libs/androidfw/ChunkIterator.cpp
+++ b/libs/androidfw/ChunkIterator.cpp
@@ -15,6 +15,7 @@
*/
#include "androidfw/Chunk.h"
+#include "androidfw/Util.h"
#include "android-base/logging.h"
@@ -23,11 +24,11 @@ namespace android {
Chunk ChunkIterator::Next() {
CHECK(len_ != 0) << "called Next() after last chunk";
- const ResChunk_header* this_chunk = next_chunk_;
+ const incfs::map_ptr<ResChunk_header> this_chunk = next_chunk_;
+ CHECK((bool) this_chunk) << "Next() called without verifying next chunk";
// We've already checked the values of this_chunk, so safely increment.
- next_chunk_ = reinterpret_cast<const ResChunk_header*>(
- reinterpret_cast<const uint8_t*>(this_chunk) + dtohl(this_chunk->size));
+ next_chunk_ = this_chunk.offset(dtohl(this_chunk->size)).convert<ResChunk_header>();
len_ -= dtohl(this_chunk->size);
if (len_ != 0) {
@@ -36,7 +37,7 @@ Chunk ChunkIterator::Next() {
VerifyNextChunk();
}
}
- return Chunk(this_chunk);
+ return Chunk(this_chunk.verified());
}
// TODO(b/111401637) remove this and have full resource file verification
@@ -47,6 +48,13 @@ bool ChunkIterator::VerifyNextChunkNonFatal() {
last_error_was_fatal_ = false;
return false;
}
+
+ if (!next_chunk_) {
+ last_error_ = "failed to read chunk from data";
+ last_error_was_fatal_ = false;
+ return false;
+ }
+
const size_t size = dtohl(next_chunk_->size);
if (size > len_) {
last_error_ = "chunk size is bigger than given data";
@@ -58,12 +66,10 @@ bool ChunkIterator::VerifyNextChunkNonFatal() {
// Returns false if there was an error.
bool ChunkIterator::VerifyNextChunk() {
- const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_);
-
// This data must be 4-byte aligned, since we directly
// access 32-bit words, which must be aligned on
// certain architectures.
- if (header_start & 0x03) {
+ if (!util::IsFourByteAligned(next_chunk_)) {
last_error_ = "header not aligned on 4-byte boundary";
return false;
}
@@ -73,6 +79,11 @@ bool ChunkIterator::VerifyNextChunk() {
return false;
}
+ if (!next_chunk_) {
+ last_error_ = "failed to read chunk from data";
+ return false;
+ }
+
const size_t header_size = dtohs(next_chunk_->headerSize);
const size_t size = dtohl(next_chunk_->size);
if (header_size < sizeof(ResChunk_header)) {
@@ -90,7 +101,7 @@ bool ChunkIterator::VerifyNextChunk() {
return false;
}
- if ((size | header_size) & 0x03) {
+ if ((size | header_size) & 0x03U) {
last_error_ = "header sizes are not aligned on 4-byte boundary";
return false;
}
diff --git a/libs/androidfw/ConfigDescription.cpp b/libs/androidfw/ConfigDescription.cpp
index 1f3a89edb8af..19ead9583eb2 100644
--- a/libs/androidfw/ConfigDescription.cpp
+++ b/libs/androidfw/ConfigDescription.cpp
@@ -887,13 +887,16 @@ bool ConfigDescription::Dominates(const ConfigDescription& o) const {
}
// Locale de-duping is not-trivial, disable for now (b/62409213).
- if (diff(o) & CONFIG_LOCALE) {
+ // We must also disable de-duping for all configuration qualifiers with precedence higher than
+ // locale (b/171892595)
+ if (diff(o) & (CONFIG_LOCALE | CONFIG_MCC | CONFIG_MNC)) {
return false;
}
if (*this == DefaultConfig()) {
return true;
}
+
return MatchWithDensity(o) && !o.MatchWithDensity(*this) &&
!isMoreSpecificThan(o) && !o.HasHigherPrecedenceThan(*this);
}
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 4e03ce5d9584..a61309514143 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -52,22 +52,22 @@ OverlayStringPool::~OverlayStringPool() {
uninit();
}
-const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
+base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const {
const size_t offset = dtohl(data_header_->string_pool_index_offset);
if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
- return idmap_string_pool_->stringAt(idx - offset, outLen);
+ return idmap_string_pool_->stringAt(idx - offset);
}
- return ResStringPool::stringAt(idx, outLen);
+ return ResStringPool::stringAt(idx);
}
-const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
+base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const {
const size_t offset = dtohl(data_header_->string_pool_index_offset);
if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
- return idmap_string_pool_->string8At(idx - offset, outLen);
+ return idmap_string_pool_->string8At(idx - offset);
}
- return ResStringPool::string8At(idx, outLen);
+ return ResStringPool::string8At(idx);
}
size_t OverlayStringPool::size() const {
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 70bb441f94cb..2fc3b05011c2 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -38,7 +38,7 @@
#include "androidfw/ResourceUtils.h"
#include "androidfw/Util.h"
-using ::android::base::StringPrintf;
+using android::base::StringPrintf;
namespace android {
@@ -51,17 +51,17 @@ namespace {
// the Type structs.
class TypeSpecPtrBuilder {
public:
- explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header)
+ explicit TypeSpecPtrBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header)
: header_(header) {
}
- void AddType(const ResTable_type* type) {
+ void AddType(incfs::verified_map_ptr<ResTable_type> type) {
types_.push_back(type);
}
TypeSpecPtr Build() {
// Check for overflow.
- using ElementType = const ResTable_type*;
+ using ElementType = incfs::verified_map_ptr<ResTable_type>;
if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) <
types_.size()) {
return {};
@@ -77,8 +77,8 @@ class TypeSpecPtrBuilder {
private:
DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
- const ResTable_typeSpec* header_;
- std::vector<const ResTable_type*> types_;
+ incfs::verified_map_ptr<ResTable_typeSpec> header_;
+ std::vector<incfs::verified_map_ptr<ResTable_type>> types_;
};
} // namespace
@@ -88,7 +88,7 @@ LoadedPackage::~LoadedPackage() = default;
// Precondition: The header passed in has already been verified, so reading any fields and trusting
// the ResChunk_header is safe.
-static bool VerifyResTableType(const ResTable_type* header) {
+static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
if (header->id == 0) {
LOG(ERROR) << "RES_TABLE_TYPE_TYPE has invalid ID 0.";
return false;
@@ -115,89 +115,99 @@ static bool VerifyResTableType(const ResTable_type* header) {
return false;
}
- if (entries_offset & 0x03) {
+ if (entries_offset & 0x03U) {
LOG(ERROR) << "RES_TABLE_TYPE_TYPE entries start at unaligned address.";
return false;
}
return true;
}
-static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) {
+static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
+ incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) {
// Check that the offset is aligned.
- if (entry_offset & 0x03) {
+ if (UNLIKELY(entry_offset & 0x03U)) {
LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned.";
- return false;
+ return base::unexpected(std::nullopt);
}
// Check that the offset doesn't overflow.
- if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) {
+ if (UNLIKELY(entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart))) {
// Overflow in offset.
LOG(ERROR) << "Entry at offset " << entry_offset << " is too large.";
- return false;
+ return base::unexpected(std::nullopt);
}
const size_t chunk_size = dtohl(type->header.size);
entry_offset += dtohl(type->entriesStart);
- if (entry_offset > chunk_size - sizeof(ResTable_entry)) {
+ if (UNLIKELY(entry_offset > chunk_size - sizeof(ResTable_entry))) {
LOG(ERROR) << "Entry at offset " << entry_offset
<< " is too large. No room for ResTable_entry.";
- return false;
+ return base::unexpected(std::nullopt);
}
- const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
- reinterpret_cast<const uint8_t*>(type) + entry_offset);
+ auto entry = type.offset(entry_offset).convert<ResTable_entry>();
+ if (UNLIKELY(!entry)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
const size_t entry_size = dtohs(entry->size);
- if (entry_size < sizeof(*entry)) {
+ if (UNLIKELY(entry_size < sizeof(entry.value()))) {
LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
<< " is too small.";
- return false;
+ return base::unexpected(std::nullopt);
}
- if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) {
+ if (UNLIKELY(entry_size > chunk_size || entry_offset > chunk_size - entry_size)) {
LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
<< " is too large.";
- return false;
+ return base::unexpected(std::nullopt);
}
if (entry_size < sizeof(ResTable_map_entry)) {
// There needs to be room for one Res_value struct.
- if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) {
+ if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) {
LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset
<< " for type " << (int)type->id << ".";
- return false;
+ return base::unexpected(std::nullopt);
+ }
+
+ auto value = entry.offset(entry_size).convert<Res_value>();
+ if (UNLIKELY(!value)) {
+ return base::unexpected(IOError::PAGES_MISSING);
}
- const Res_value* value =
- reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size);
const size_t value_size = dtohs(value->size);
- if (value_size < sizeof(Res_value)) {
+ if (UNLIKELY(value_size < sizeof(Res_value))) {
LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small.";
- return false;
+ return base::unexpected(std::nullopt);
}
- if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) {
+ if (UNLIKELY(value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size)) {
LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset
<< " is too large.";
- return false;
+ return base::unexpected(std::nullopt);
}
} else {
- const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry);
+ auto map = entry.convert<ResTable_map_entry>();
+ if (UNLIKELY(!map)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
const size_t map_entry_count = dtohl(map->count);
size_t map_entries_start = entry_offset + entry_size;
- if (map_entries_start & 0x03) {
+ if (UNLIKELY(map_entries_start & 0x03U)) {
LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset.";
- return false;
+ return base::unexpected(std::nullopt);
}
// Each entry is sizeof(ResTable_map) big.
- if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) {
+ if (UNLIKELY(map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map)))) {
LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << ".";
- return false;
+ return base::unexpected(std::nullopt);
}
}
- return true;
+ return {};
}
LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei)
@@ -233,99 +243,125 @@ uint32_t LoadedPackage::iterator::operator*() const {
entryIndex_);
}
-const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk,
- uint16_t entry_index) {
- uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index);
- if (entry_offset == ResTable_type::NO_ENTRY) {
- return nullptr;
+base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
+ base::expected<uint32_t, NullOrIOError> entry_offset = GetEntryOffset(type_chunk, entry_index);
+ if (UNLIKELY(!entry_offset.has_value())) {
+ return base::unexpected(entry_offset.error());
}
- return GetEntryFromOffset(type_chunk, entry_offset);
+ return GetEntryFromOffset(type_chunk, entry_offset.value());
}
-uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) {
+base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
const size_t entry_count = dtohl(type_chunk->entryCount);
const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
// Check if there is the desired entry in this type.
-
if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
// This is encoded as a sparse map, so perform a binary search.
- const ResTable_sparseTypeEntry* sparse_indices =
- reinterpret_cast<const ResTable_sparseTypeEntry*>(
- reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
- const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count;
- const ResTable_sparseTypeEntry* result =
- std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
- [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) {
- return dtohs(entry.idx) < entry_idx;
- });
-
- if (result == sparse_indices_end || dtohs(result->idx) != entry_index) {
+ bool error = false;
+ auto sparse_indices = type_chunk.offset(offsets_offset)
+ .convert<ResTable_sparseTypeEntry>().iterator();
+ auto sparse_indices_end = sparse_indices + entry_count;
+ auto result = std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
+ [&error](const incfs::map_ptr<ResTable_sparseTypeEntry>& entry,
+ uint16_t entry_idx) {
+ if (UNLIKELY(!entry)) {
+ return error = true;
+ }
+ return dtohs(entry->idx) < entry_idx;
+ });
+
+ if (result == sparse_indices_end) {
// No entry found.
- return ResTable_type::NO_ENTRY;
+ return base::unexpected(std::nullopt);
+ }
+
+ const incfs::verified_map_ptr<ResTable_sparseTypeEntry> entry = (*result).verified();
+ if (dtohs(entry->idx) != entry_index) {
+ if (error) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ return base::unexpected(std::nullopt);
}
// Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
// the real offset divided by 4.
- return uint32_t{dtohs(result->offset)} * 4u;
+ return uint32_t{dtohs(entry->offset)} * 4u;
}
// This type is encoded as a dense array.
if (entry_index >= entry_count) {
// This entry cannot be here.
- return ResTable_type::NO_ENTRY;
+ return base::unexpected(std::nullopt);
}
- const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
- reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
- return dtohl(entry_offsets[entry_index]);
+ const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert<uint32_t>() + entry_index;
+ if (UNLIKELY(!entry_offset_ptr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ const uint32_t value = dtohl(entry_offset_ptr.value());
+ if (value == ResTable_type::NO_ENTRY) {
+ return base::unexpected(std::nullopt);
+ }
+
+ return value;
}
-const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk,
- uint32_t offset) {
- if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) {
- return nullptr;
+base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntryFromOffset(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset) {
+ auto valid = VerifyResTableEntry(type_chunk, offset);
+ if (UNLIKELY(!valid.has_value())) {
+ return base::unexpected(valid.error());
}
- return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) +
- offset + dtohl(type_chunk->entriesStart));
+ return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert<ResTable_entry>();
}
-void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
- std::set<ResTable_config>* out_configs) const {
- const static std::u16string kMipMap = u"mipmap";
+base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations(
+ bool exclude_mipmap, std::set<ResTable_config>* out_configs) const {
const size_t type_count = type_specs_.size();
for (size_t i = 0; i < type_count; i++) {
const TypeSpecPtr& type_spec = type_specs_[i];
- if (type_spec != nullptr) {
- if (exclude_mipmap) {
- const int type_idx = type_spec->type_spec->id - 1;
- size_t type_name_len;
- const char16_t* type_name16 = type_string_pool_.stringAt(type_idx, &type_name_len);
- if (type_name16 != nullptr) {
- if (kMipMap.compare(0, std::u16string::npos, type_name16, type_name_len) == 0) {
- // This is a mipmap type, skip collection.
- continue;
- }
- }
- const char* type_name = type_string_pool_.string8At(type_idx, &type_name_len);
- if (type_name != nullptr) {
- if (strncmp(type_name, "mipmap", type_name_len) == 0) {
- // This is a mipmap type, skip collection.
- continue;
- }
+ if (type_spec == nullptr) {
+ continue;
+ }
+ if (exclude_mipmap) {
+ const int type_idx = type_spec->type_spec->id - 1;
+ const auto type_name16 = type_string_pool_.stringAt(type_idx);
+ if (UNLIKELY(IsIOError(type_name16))) {
+ return base::unexpected(GetIOError(type_name16.error()));
+ }
+ if (type_name16.has_value()) {
+ if (strncmp16(type_name16->data(), u"mipmap", type_name16->size()) == 0) {
+ // This is a mipmap type, skip collection.
+ continue;
}
}
- const auto iter_end = type_spec->types + type_spec->type_count;
- for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- ResTable_config config;
- config.copyFromDtoH((*iter)->config);
- out_configs->insert(config);
+ const auto type_name = type_string_pool_.string8At(type_idx);
+ if (UNLIKELY(IsIOError(type_name))) {
+ return base::unexpected(GetIOError(type_name.error()));
}
+ if (type_name.has_value()) {
+ if (strncmp(type_name->data(), "mipmap", type_name->size()) == 0) {
+ // This is a mipmap type, skip collection.
+ continue;
+ }
+ }
+ }
+
+ const auto iter_end = type_spec->types + type_spec->type_count;
+ for (auto iter = type_spec->types; iter != iter_end; ++iter) {
+ ResTable_config config;
+ config.copyFromDtoH((*iter)->config);
+ out_configs->insert(config);
}
}
+ return {};
}
void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const {
@@ -348,43 +384,53 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out
}
}
-uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name,
- const std::u16string& entry_name) const {
- ssize_t type_idx = type_string_pool_.indexOfString(type_name.data(), type_name.size());
- if (type_idx < 0) {
- return 0u;
+base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName(
+ const std::u16string& type_name, const std::u16string& entry_name) const {
+ const base::expected<size_t, NullOrIOError> type_idx = type_string_pool_.indexOfString(
+ type_name.data(), type_name.size());
+ if (!type_idx.has_value()) {
+ return base::unexpected(type_idx.error());
}
- ssize_t key_idx = key_string_pool_.indexOfString(entry_name.data(), entry_name.size());
- if (key_idx < 0) {
- return 0u;
+ const base::expected<size_t, NullOrIOError> key_idx = key_string_pool_.indexOfString(
+ entry_name.data(), entry_name.size());
+ if (!key_idx.has_value()) {
+ return base::unexpected(key_idx.error());
}
- const TypeSpec* type_spec = type_specs_[type_idx].get();
+ const TypeSpec* type_spec = type_specs_[*type_idx].get();
if (type_spec == nullptr) {
- return 0u;
+ return base::unexpected(std::nullopt);
}
const auto iter_end = type_spec->types + type_spec->type_count;
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- const ResTable_type* type = *iter;
+ const incfs::verified_map_ptr<ResTable_type>& type = *iter;
+
size_t entry_count = dtohl(type->entryCount);
for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
- const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
- reinterpret_cast<const uint8_t*>(type) + dtohs(type->header.headerSize));
- const uint32_t offset = dtohl(entry_offsets[entry_idx]);
+ auto entry_offset_ptr = type.offset(dtohs(type->header.headerSize)).convert<uint32_t>() +
+ entry_idx;
+ if (!entry_offset_ptr) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ auto offset = dtohl(entry_offset_ptr.value());
if (offset != ResTable_type::NO_ENTRY) {
- const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
- reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset);
- if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) {
+ auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
+ if (!entry) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
// The package ID will be overridden by the caller (due to runtime assignment of package
// IDs for shared libraries).
- return make_resid(0x00, type_idx + type_id_offset_ + 1, entry_idx);
+ return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx);
}
}
}
}
- return 0u;
+ return base::unexpected(std::nullopt);
}
const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
@@ -405,8 +451,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
// was added.
constexpr size_t kMinPackageSize =
sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset);
- const ResTable_package* header = chunk.header<ResTable_package, kMinPackageSize>();
- if (header == nullptr) {
+ const incfs::map_ptr<ResTable_package> header = chunk.header<ResTable_package, kMinPackageSize>();
+ if (!header) {
LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small.";
return {};
}
@@ -453,10 +499,13 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
const Chunk child_chunk = iter.Next();
switch (child_chunk.type()) {
case RES_STRING_POOL_TYPE: {
- const uintptr_t pool_address =
- reinterpret_cast<uintptr_t>(child_chunk.header<ResChunk_header>());
- const uintptr_t header_address = reinterpret_cast<uintptr_t>(header);
- if (pool_address == header_address + dtohl(header->typeStrings)) {
+ const auto pool_address = child_chunk.header<ResChunk_header>();
+ if (!pool_address) {
+ LOG(ERROR) << "RES_STRING_POOL_TYPE is incomplete due to incremental installation.";
+ return {};
+ }
+
+ if (pool_address == header.offset(dtohl(header->typeStrings)).convert<ResChunk_header>()) {
// This string pool is the type string pool.
status_t err = loaded_package->type_string_pool_.setTo(
child_chunk.header<ResStringPool_header>(), child_chunk.size());
@@ -464,7 +513,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt.";
return {};
}
- } else if (pool_address == header_address + dtohl(header->keyStrings)) {
+ } else if (pool_address == header.offset(dtohl(header->keyStrings))
+ .convert<ResChunk_header>()) {
// This string pool is the key string pool.
status_t err = loaded_package->key_string_pool_.setTo(
child_chunk.header<ResStringPool_header>(), child_chunk.size());
@@ -478,8 +528,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
} break;
case RES_TABLE_TYPE_SPEC_TYPE: {
- const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>();
- if (type_spec == nullptr) {
+ const auto type_spec = child_chunk.header<ResTable_typeSpec>();
+ if (!type_spec) {
LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small.";
return {};
}
@@ -514,7 +564,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1];
if (builder_ptr == nullptr) {
- builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec);
+ builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec.verified());
loaded_package->resource_ids_.set(type_spec->id, entry_count);
} else {
LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
@@ -523,8 +573,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
} break;
case RES_TABLE_TYPE_TYPE: {
- const ResTable_type* type = child_chunk.header<ResTable_type, kResTableTypeMinSize>();
- if (type == nullptr) {
+ const auto type = child_chunk.header<ResTable_type, kResTableTypeMinSize>();
+ if (!type) {
LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small.";
return {};
}
@@ -536,7 +586,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
// Type chunks must be preceded by their TypeSpec chunks.
std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type->id - 1];
if (builder_ptr != nullptr) {
- builder_ptr->AddType(type);
+ builder_ptr->AddType(type.verified());
} else {
LOG(ERROR) << StringPrintf(
"RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.",
@@ -546,8 +596,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
} break;
case RES_TABLE_LIBRARY_TYPE: {
- const ResTable_lib_header* lib = child_chunk.header<ResTable_lib_header>();
- if (lib == nullptr) {
+ const auto lib = child_chunk.header<ResTable_lib_header>();
+ if (!lib) {
LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small.";
return {};
}
@@ -559,10 +609,13 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
loaded_package->dynamic_package_map_.reserve(dtohl(lib->count));
- const ResTable_lib_entry* const entry_begin =
- reinterpret_cast<const ResTable_lib_entry*>(child_chunk.data_ptr());
- const ResTable_lib_entry* const entry_end = entry_begin + dtohl(lib->count);
+ const auto entry_begin = child_chunk.data_ptr().convert<ResTable_lib_entry>();
+ const auto entry_end = entry_begin + dtohl(lib->count);
for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
+ if (!entry_iter) {
+ return {};
+ }
+
std::string package_name;
util::ReadUtf16StringFromDevice(entry_iter->packageName,
arraysize(entry_iter->packageName), &package_name);
@@ -580,17 +633,16 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
} break;
case RES_TABLE_OVERLAYABLE_TYPE: {
- const ResTable_overlayable_header* header =
- child_chunk.header<ResTable_overlayable_header>();
- if (header == nullptr) {
+ const auto overlayable = child_chunk.header<ResTable_overlayable_header>();
+ if (!overlayable) {
LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small.";
return {};
}
std::string name;
- util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &name);
+ util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name);
std::string actor;
- util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor);
+ util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor);
if (loaded_package->overlayable_map_.find(name) !=
loaded_package->overlayable_map_.end()) {
@@ -606,9 +658,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
switch (overlayable_child_chunk.type()) {
case RES_TABLE_OVERLAYABLE_POLICY_TYPE: {
- const ResTable_overlayable_policy_header* policy_header =
+ const auto policy_header =
overlayable_child_chunk.header<ResTable_overlayable_policy_header>();
- if (policy_header == nullptr) {
+ if (!policy_header) {
LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
return {};
}
@@ -621,10 +673,12 @@ 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 =
- reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr());
+ const auto ids_begin = overlayable_child_chunk.data_ptr().convert<ResTable_ref>();
const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
+ if (!id_iter) {
+ return {};
+ }
ids.insert(dtohl(id_iter->ident));
}
@@ -633,7 +687,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
overlayable_info.name = name;
overlayable_info.actor = actor;
overlayable_info.policy_flags = policy_header->policy_flags;
- loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
+ loaded_package->overlayable_infos_.emplace_back(overlayable_info, ids);
loaded_package->defines_overlayable_ = true;
break;
}
@@ -683,8 +737,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
package_property_t property_flags) {
- const ResTable_header* header = chunk.header<ResTable_header>();
- if (header == nullptr) {
+ incfs::map_ptr<ResTable_header> header = chunk.header<ResTable_header>();
+ if (!header) {
LOG(ERROR) << "RES_TABLE_TYPE too small.";
return false;
}
@@ -747,7 +801,8 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
return true;
}
-std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
+std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
+ const size_t length,
const LoadedIdmap* loaded_idmap,
const package_property_t property_flags) {
ATRACE_NAME("LoadedArsc::Load");
@@ -755,7 +810,7 @@ std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
// Not using make_unique because the constructor is private.
std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
- ChunkIterator iter(data.data(), data.size());
+ ChunkIterator iter(data, length);
while (iter.HasNext()) {
const Chunk chunk = iter.Next();
switch (chunk.type()) {
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index dfb4009b07e2..4010e4e10317 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -104,22 +104,26 @@ static void strcpy16_dtoh(char16_t* dst, const uint16_t* src, size_t avail)
*dst = 0;
}
-static status_t validate_chunk(const ResChunk_header* chunk,
+static status_t validate_chunk(const incfs::map_ptr<ResChunk_header>& chunk,
size_t minSize,
- const uint8_t* dataEnd,
+ const incfs::map_ptr<uint8_t> dataEnd,
const char* name)
{
+ if (!chunk) {
+ return BAD_TYPE;
+ }
+
const uint16_t headerSize = dtohs(chunk->headerSize);
const uint32_t size = dtohl(chunk->size);
if (headerSize >= minSize) {
if (headerSize <= size) {
if (((headerSize|size)&0x3) == 0) {
- if ((size_t)size <= (size_t)(dataEnd-((const uint8_t*)chunk))) {
+ if ((size_t)size <= (size_t)(dataEnd-chunk.convert<uint8_t>())) {
return NO_ERROR;
}
ALOGW("%s data size 0x%x extends beyond resource end %p.",
- name, size, (void*)(dataEnd-((const uint8_t*)chunk)));
+ name, size, (void*)(dataEnd-chunk.convert<uint8_t>()));
return BAD_TYPE;
}
ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.",
@@ -450,7 +454,7 @@ void ResStringPool::setToEmpty()
mHeader = (const ResStringPool_header*) header;
}
-status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
+status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyData)
{
if (!data || !size) {
return (mError=BAD_TYPE);
@@ -467,8 +471,8 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
// The data is at least as big as a ResChunk_header, so we can safely validate the other
// header fields.
// `data + size` is safe because the source of `size` comes from the kernel/filesystem.
- if (validate_chunk(reinterpret_cast<const ResChunk_header*>(data), sizeof(ResStringPool_header),
- reinterpret_cast<const uint8_t*>(data) + size,
+ const auto chunk_header = data.convert<ResChunk_header>();
+ if (validate_chunk(chunk_header, sizeof(ResStringPool_header), data.convert<uint8_t>() + size,
"ResStringPool_header") != NO_ERROR) {
ALOGW("Bad string block: malformed block dimensions");
return (mError=BAD_TYPE);
@@ -481,16 +485,25 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
if (mOwnedData == NULL) {
return (mError=NO_MEMORY);
}
- memcpy(mOwnedData, data, size);
+
+ if (!data.convert<uint8_t>().verify(size)) {
+ return (mError=NO_MEMORY);
+ }
+
+ memcpy(mOwnedData, data.unsafe_ptr(), size);
data = mOwnedData;
}
// The size has been checked, so it is safe to read the data in the ResStringPool_header
// data structure.
- mHeader = (const ResStringPool_header*)data;
+ const auto header = data.convert<ResStringPool_header>();
+ if (!header) {
+ return (mError=BAD_TYPE);
+ }
+ mHeader = header.verified();
if (notDeviceEndian) {
- ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader);
+ ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader.unsafe_ptr());
h->header.headerSize = dtohs(mHeader->header.headerSize);
h->header.type = dtohs(mHeader->header.type);
h->header.size = dtohl(mHeader->header.size);
@@ -508,8 +521,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
return (mError=BAD_TYPE);
}
mSize = mHeader->header.size;
- mEntries = (const uint32_t*)
- (((const uint8_t*)data)+mHeader->header.headerSize);
+ mEntries = data.offset(mHeader->header.headerSize).convert<uint32_t>();
if (mHeader->stringCount > 0) {
if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow?
@@ -536,9 +548,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
return (mError=BAD_TYPE);
}
- mStrings = (const void*)
- (((const uint8_t*)data) + mHeader->stringsStart);
-
+ mStrings = data.offset(mHeader->stringsStart).convert<void>();
if (mHeader->styleCount == 0) {
mStringPoolSize = (mSize - mHeader->stringsStart) / charSize;
} else {
@@ -560,31 +570,37 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
// check invariant: stringCount > 0 requires a string pool to exist
if (mStringPoolSize == 0) {
- ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount);
+ ALOGW("Bad string block: stringCount is %d but pool size is 0\n",
+ (int)mHeader->stringCount);
return (mError=BAD_TYPE);
}
if (notDeviceEndian) {
size_t i;
- uint32_t* e = const_cast<uint32_t*>(mEntries);
+ auto e = const_cast<uint32_t*>(mEntries.unsafe_ptr());
for (i=0; i<mHeader->stringCount; i++) {
- e[i] = dtohl(mEntries[i]);
+ e[i] = dtohl(e[i]);
}
if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) {
- const uint16_t* strings = (const uint16_t*)mStrings;
- uint16_t* s = const_cast<uint16_t*>(strings);
+ uint16_t* s = const_cast<uint16_t*>(mStrings.convert<uint16_t>().unsafe_ptr());
for (i=0; i<mStringPoolSize; i++) {
- s[i] = dtohs(strings[i]);
+ s[i] = dtohs(s[i]);
}
}
}
- if ((mHeader->flags&ResStringPool_header::UTF8_FLAG &&
- ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) ||
- (!(mHeader->flags&ResStringPool_header::UTF8_FLAG) &&
- ((uint16_t*)mStrings)[mStringPoolSize-1] != 0)) {
- ALOGW("Bad string block: last string is not 0-terminated\n");
- return (mError=BAD_TYPE);
+ if (mHeader->flags&ResStringPool_header::UTF8_FLAG) {
+ auto end = mStrings.convert<uint8_t>() + (mStringPoolSize-1);
+ if (!end || end.value() != 0) {
+ ALOGW("Bad string block: last string is not 0-terminated\n");
+ return (mError=BAD_TYPE);
+ }
+ } else {
+ auto end = mStrings.convert<uint16_t>() + (mStringPoolSize-1);
+ if (!end || end.value() != 0) {
+ ALOGW("Bad string block: last string is not 0-terminated\n");
+ return (mError=BAD_TYPE);
+ }
}
} else {
mStrings = NULL;
@@ -599,14 +615,13 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
return (mError=BAD_TYPE);
}
- if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) {
+ if ((mEntryStyles.convert<uint8_t>() - mHeader.convert<uint8_t>()) > (int)size) {
ALOGW("Bad string block: entry of %d styles extends past data size %d\n",
- (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader),
+ (int)(mEntryStyles.convert<uint8_t>()-mHeader.convert<uint8_t>()),
(int)size);
return (mError=BAD_TYPE);
}
- mStyles = (const uint32_t*)
- (((const uint8_t*)data)+mHeader->stylesStart);
+ mStyles = data.offset(mHeader->stylesStart).convert<uint32_t>();
if (mHeader->stylesStart >= mHeader->header.size) {
ALOGW("Bad string block: style pool starts %d, after total size %d\n",
(int)mHeader->stylesStart, (int)mHeader->header.size);
@@ -617,13 +632,13 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
if (notDeviceEndian) {
size_t i;
- uint32_t* e = const_cast<uint32_t*>(mEntryStyles);
+ uint32_t* e = const_cast<uint32_t*>(mEntryStyles.unsafe_ptr());
for (i=0; i<mHeader->styleCount; i++) {
- e[i] = dtohl(mEntryStyles[i]);
+ e[i] = dtohl(e[i]);
}
- uint32_t* s = const_cast<uint32_t*>(mStyles);
+ uint32_t* s = const_cast<uint32_t*>(mStyles.unsafe_ptr());
for (i=0; i<mStylePoolSize; i++) {
- s[i] = dtohl(mStyles[i]);
+ s[i] = dtohl(s[i]);
}
}
@@ -631,8 +646,9 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
{ htodl(ResStringPool_span::END) },
htodl(ResStringPool_span::END), htodl(ResStringPool_span::END)
};
- if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))],
- &endSpan, sizeof(endSpan)) != 0) {
+
+ auto stylesEnd = mStyles + (mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t)));
+ if (!stylesEnd || memcmp(stylesEnd.unsafe_ptr(), &endSpan, sizeof(endSpan)) != 0) {
ALOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n");
return (mError=BAD_TYPE);
}
@@ -653,7 +669,7 @@ status_t ResStringPool::getError() const
void ResStringPool::uninit()
{
mError = NO_INIT;
- if (mHeader != NULL && mCache != NULL) {
+ if (mHeader && mCache != NULL) {
for (size_t x = 0; x < mHeader->stringCount; x++) {
if (mCache[x] != NULL) {
free(mCache[x]);
@@ -679,15 +695,21 @@ void ResStringPool::uninit()
* data encoded. In that case, drop the high bit of the first character and
* add it together with the next character.
*/
-static inline size_t
-decodeLength(const uint16_t** str)
+static inline base::expected<size_t, IOError> decodeLength(incfs::map_ptr<uint16_t>* str)
{
- size_t len = **str;
- if ((len & 0x8000) != 0) {
- (*str)++;
- len = ((len & 0x7FFF) << 16) | **str;
+ if (UNLIKELY(!*str)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ size_t len = str->value();
+ if ((len & 0x8000U) != 0) {
+ ++(*str);
+ if (UNLIKELY(!*str)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ len = ((len & 0x7FFFU) << 16U) | str->value();
}
- (*str)++;
+ ++(*str);
return len;
}
@@ -701,82 +723,119 @@ decodeLength(const uint16_t** str)
* data encoded. In that case, drop the high bit of the first character and
* add it together with the next character.
*/
-static inline size_t
-decodeLength(const uint8_t** str)
+static inline base::expected<size_t, IOError> decodeLength(incfs::map_ptr<uint8_t>* str)
{
- size_t len = **str;
- if ((len & 0x80) != 0) {
- (*str)++;
- len = ((len & 0x7F) << 8) | **str;
+ if (UNLIKELY(!*str)) {
+ return base::unexpected(IOError::PAGES_MISSING);
}
- (*str)++;
+
+ size_t len = str->value();
+ if ((len & 0x80U) != 0) {
+ ++(*str);
+ if (UNLIKELY(!*str)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ len = ((len & 0x7FU) << 8U) | str->value();
+ }
+ ++(*str);
return len;
}
-const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
+base::expected<StringPiece16, NullOrIOError> ResStringPool::stringAt(size_t idx) const
{
if (mError == NO_ERROR && idx < mHeader->stringCount) {
const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
- const uint32_t off = mEntries[idx]/(isUTF8?sizeof(uint8_t):sizeof(uint16_t));
+ auto offPtr = mEntries + idx;
+ if (UNLIKELY(!offPtr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ const uint32_t off = (offPtr.value())/(isUTF8?sizeof(uint8_t):sizeof(uint16_t));
if (off < (mStringPoolSize-1)) {
if (!isUTF8) {
- const uint16_t* strings = (uint16_t*)mStrings;
- const uint16_t* str = strings+off;
+ auto strings = mStrings.convert<uint16_t>();
+ auto str = strings+off;
+
+ const base::expected<size_t, IOError> u16len = decodeLength(&str);
+ if (UNLIKELY(!u16len.has_value())) {
+ return base::unexpected(u16len.error());
+ }
- *u16len = decodeLength(&str);
if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) {
// Reject malformed (non null-terminated) strings
- if (str[*u16len] != 0x0000) {
- ALOGW("Bad string block: string #%d is not null-terminated",
- (int)idx);
- return NULL;
+ const auto nullAddress = str + (*u16len);
+ if (UNLIKELY(!nullAddress)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ if (nullAddress.value() != 0x0000) {
+ ALOGW("Bad string block: string #%d is not null-terminated", (int)idx);
+ return base::unexpected(std::nullopt);
+ }
+
+ if (UNLIKELY(!str.verify(*u16len + 1U))) {
+ return base::unexpected(IOError::PAGES_MISSING);
}
- return reinterpret_cast<const char16_t*>(str);
+
+ return StringPiece16(reinterpret_cast<const char16_t*>(str.unsafe_ptr()),
+ *u16len);
} else {
ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
(int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize);
}
} else {
- const uint8_t* strings = (uint8_t*)mStrings;
- const uint8_t* u8str = strings+off;
+ auto strings = mStrings.convert<uint8_t>();
+ auto u8str = strings+off;
- *u16len = decodeLength(&u8str);
- size_t u8len = decodeLength(&u8str);
+ base::expected<size_t, IOError> u16len = decodeLength(&u8str);
+ if (UNLIKELY(!u16len.has_value())) {
+ return base::unexpected(u16len.error());
+ }
+
+ const base::expected<size_t, IOError> u8len = decodeLength(&u8str);
+ if (UNLIKELY(!u8len.has_value())) {
+ return base::unexpected(u8len.error());
+ }
// encLen must be less than 0x7FFF due to encoding.
- if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) {
+ if ((uint32_t)(u8str+*u8len-strings) < mStringPoolSize) {
AutoMutex lock(mDecodeLock);
if (mCache != NULL && mCache[idx] != NULL) {
- return mCache[idx];
+ return StringPiece16(mCache[idx], *u16len);
}
// Retrieve the actual length of the utf8 string if the
// encoded length was truncated
- if (stringDecodeAt(idx, u8str, u8len, &u8len) == NULL) {
- return NULL;
+ auto decodedString = stringDecodeAt(idx, u8str, *u8len);
+ if (!decodedString.has_value()) {
+ return base::unexpected(decodedString.error());
}
// Since AAPT truncated lengths longer than 0x7FFF, check
// that the bits that remain after truncation at least match
// the bits of the actual length
- ssize_t actualLen = utf8_to_utf16_length(u8str, u8len);
- if (actualLen < 0 || ((size_t)actualLen & 0x7FFF) != *u16len) {
+ ssize_t actualLen = utf8_to_utf16_length(
+ reinterpret_cast<const uint8_t*>(decodedString->data()),
+ decodedString->size());
+
+ if (actualLen < 0 || ((size_t)actualLen & 0x7FFFU) != *u16len) {
ALOGW("Bad string block: string #%lld decoded length is not correct "
"%lld vs %llu\n",
(long long)idx, (long long)actualLen, (long long)*u16len);
- return NULL;
+ return base::unexpected(std::nullopt);
}
- *u16len = (size_t) actualLen;
- char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t));
+ u16len = (size_t) actualLen;
+ auto u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t));
if (!u16str) {
ALOGW("No memory when trying to allocate decode cache for string #%d\n",
(int)idx);
- return NULL;
+ return base::unexpected(std::nullopt);
}
- utf8_to_utf16(u8str, u8len, u16str, *u16len + 1);
+ utf8_to_utf16(reinterpret_cast<const uint8_t*>(decodedString->data()),
+ decodedString->size(), u16str, *u16len + 1);
if (mCache == NULL) {
#ifndef __ANDROID__
@@ -793,19 +852,19 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
if (mCache == NULL) {
ALOGW("No memory trying to allocate decode cache table of %d bytes\n",
(int)(mHeader->stringCount*sizeof(char16_t**)));
- return NULL;
+ return base::unexpected(std::nullopt);
}
}
if (kDebugStringPoolNoisy) {
- ALOGI("Caching UTF8 string: %s", u8str);
+ ALOGI("Caching UTF8 string: %s", u8str.unsafe_ptr());
}
mCache[idx] = u16str;
- return u16str;
+ return StringPiece16(u16str, *u16len);
} else {
ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n",
- (long long)idx, (long long)(u8str+u8len-strings),
+ (long long)idx, (long long)(u8str+*u8len-strings),
(long long)mStringPoolSize);
}
}
@@ -815,33 +874,43 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
(int)(mStringPoolSize*sizeof(uint16_t)));
}
}
- return NULL;
+ return base::unexpected(std::nullopt);
}
-const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
+base::expected<StringPiece, NullOrIOError> ResStringPool::string8At(size_t idx) const
{
if (mError == NO_ERROR && idx < mHeader->stringCount) {
if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) {
- return NULL;
+ return base::unexpected(std::nullopt);
}
- const uint32_t off = mEntries[idx]/sizeof(char);
+
+ auto offPtr = mEntries + idx;
+ if (UNLIKELY(!offPtr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ const uint32_t off = (offPtr.value())/sizeof(char);
if (off < (mStringPoolSize-1)) {
- const uint8_t* strings = (uint8_t*)mStrings;
- const uint8_t* str = strings+off;
+ auto strings = mStrings.convert<uint8_t>();
+ auto str = strings+off;
// Decode the UTF-16 length. This is not used if we're not
// converting to UTF-16 from UTF-8.
- decodeLength(&str);
-
- const size_t encLen = decodeLength(&str);
- *outLen = encLen;
+ const base::expected<size_t, IOError> u16len = decodeLength(&str);
+ if (UNLIKELY(!u16len)) {
+ return base::unexpected(u16len.error());
+ }
- if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
- return stringDecodeAt(idx, str, encLen, outLen);
+ const base::expected<size_t, IOError> u8len = decodeLength(&str);
+ if (UNLIKELY(!u8len)) {
+ return base::unexpected(u8len.error());
+ }
+ if ((uint32_t)(str+*u8len-strings) < mStringPoolSize) {
+ return stringDecodeAt(idx, str, *u8len);
} else {
ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
- (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
+ (int)idx, (int)(str+*u8len-strings), (int)mStringPoolSize);
}
} else {
ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
@@ -849,7 +918,7 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
(int)(mStringPoolSize*sizeof(uint16_t)));
}
}
- return NULL;
+ return base::unexpected(std::nullopt);
}
/**
@@ -859,74 +928,93 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const
* bits. Strings that exceed the maximum encode length are not placed into
* StringPools in AAPT2.
**/
-const char* ResStringPool::stringDecodeAt(size_t idx, const uint8_t* str,
- const size_t encLen, size_t* outLen) const {
- const uint8_t* strings = (uint8_t*)mStrings;
-
+base::expected<StringPiece, NullOrIOError> ResStringPool::stringDecodeAt(
+ size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const
+{
+ const auto strings = mStrings.convert<uint8_t>();
size_t i = 0, end = encLen;
while ((uint32_t)(str+end-strings) < mStringPoolSize) {
- if (str[end] == 0x00) {
+ const auto nullAddress = str + end;
+ if (UNLIKELY(!nullAddress)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ if (nullAddress.value() == 0x00) {
if (i != 0) {
ALOGW("Bad string block: string #%d is truncated (actual length is %d)",
(int)idx, (int)end);
}
- *outLen = end;
- return (const char*)str;
+ if (UNLIKELY(!str.verify(end + 1U))) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ return StringPiece((const char*) str.unsafe_ptr(), end);
}
end = (++i << (sizeof(uint8_t) * 8 * 2 - 1)) | encLen;
}
// Reject malformed (non null-terminated) strings
- ALOGW("Bad string block: string #%d is not null-terminated",
- (int)idx);
- return NULL;
+ ALOGW("Bad string block: string #%d is not null-terminated", (int)idx);
+ return base::unexpected(std::nullopt);
}
-const String8 ResStringPool::string8ObjectAt(size_t idx) const
+base::expected<String8, IOError> ResStringPool::string8ObjectAt(size_t idx) const
{
- size_t len;
- const char *str = string8At(idx, &len);
- if (str != NULL) {
- return String8(str, len);
+ const base::expected<StringPiece, NullOrIOError> str = string8At(idx);
+ if (UNLIKELY(IsIOError(str))) {
+ return base::unexpected(GetIOError(str.error()));
+ }
+ if (str.has_value()) {
+ return String8(str->data(), str->size());
}
- const char16_t *str16 = stringAt(idx, &len);
- if (str16 != NULL) {
- return String8(str16, len);
+ const base::expected<StringPiece16, NullOrIOError> str16 = stringAt(idx);
+ if (UNLIKELY(IsIOError(str16))) {
+ return base::unexpected(GetIOError(str16.error()));
}
+ if (str16.has_value()) {
+ return String8(str16->data(), str16->size());
+ }
+
return String8();
}
-const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const
+base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool::styleAt(
+ const ResStringPool_ref& ref) const
{
return styleAt(ref.index);
}
-const ResStringPool_span* ResStringPool::styleAt(size_t idx) const
+base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool::styleAt(
+ size_t idx) const
{
if (mError == NO_ERROR && idx < mHeader->styleCount) {
- const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t));
+ auto offPtr = mEntryStyles + idx;
+ if (UNLIKELY(!offPtr)) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+
+ const uint32_t off = ((offPtr.value())/sizeof(uint32_t));
if (off < mStylePoolSize) {
- return (const ResStringPool_span*)(mStyles+off);
+ return (mStyles+off).convert<ResStringPool_span>();
} else {
ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n",
(int)idx, (int)(off*sizeof(uint32_t)),
(int)(mStylePoolSize*sizeof(uint32_t)));
}
}
- return NULL;
+ return base::unexpected(std::nullopt);
}
-ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
+base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_t* str,
+ size_t strLen) const
{
if (mError != NO_ERROR) {
- return mError;
+ return base::unexpected(std::nullopt);
}
- size_t len;
-
if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) {
if (kDebugStringPoolNoisy) {
ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string());
@@ -948,17 +1036,19 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
ssize_t mid;
while (l <= h) {
mid = l + (h - l)/2;
- const uint8_t* s = (const uint8_t*)string8At(mid, &len);
- int c;
- if (s != NULL) {
- char16_t* end = utf8_to_utf16(s, len, convBuffer, convBufferLen);
+ int c = -1;
+ const base::expected<StringPiece, NullOrIOError> s = string8At(mid);
+ if (UNLIKELY(IsIOError(s))) {
+ return base::unexpected(s.error());
+ }
+ if (s.has_value()) {
+ char16_t* end = utf8_to_utf16(reinterpret_cast<const uint8_t*>(s->data()),
+ s->size(), convBuffer, convBufferLen);
c = strzcmp16(convBuffer, end-convBuffer, str, strLen);
- } else {
- c = -1;
}
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
- (const char*)s, c, (int)l, (int)mid, (int)h);
+ s->data(), c, (int)l, (int)mid, (int)h);
}
if (c == 0) {
if (kDebugStringPoolNoisy) {
@@ -981,15 +1071,21 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
String8 str8(str, strLen);
const size_t str8Len = str8.size();
for (int i=mHeader->stringCount-1; i>=0; i--) {
- const char* s = string8At(i, &len);
- if (kDebugStringPoolNoisy) {
- ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
+ const base::expected<StringPiece, NullOrIOError> s = string8At(i);
+ if (UNLIKELY(IsIOError(s))) {
+ return base::unexpected(s.error());
}
- if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) {
+ if (s.has_value()) {
if (kDebugStringPoolNoisy) {
- ALOGI("MATCH!");
+ ALOGI("Looking at %s, i=%d\n", s->data(), i);
+ }
+ if (str8Len == s->size()
+ && memcmp(s->data(), str8.string(), str8Len) == 0) {
+ if (kDebugStringPoolNoisy) {
+ ALOGI("MATCH!");
+ }
+ return i;
}
- return i;
}
}
}
@@ -1007,11 +1103,14 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
ssize_t mid;
while (l <= h) {
mid = l + (h - l)/2;
- const char16_t* s = stringAt(mid, &len);
- int c = s ? strzcmp16(s, len, str, strLen) : -1;
+ const base::expected<StringPiece16, NullOrIOError> s = stringAt(mid);
+ if (UNLIKELY(IsIOError(s))) {
+ return base::unexpected(s.error());
+ }
+ int c = s.has_value() ? strzcmp16(s->data(), s->size(), str, strLen) : -1;
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
- String8(s).string(), c, (int)l, (int)mid, (int)h);
+ String8(s->data(), s->size()).string(), c, (int)l, (int)mid, (int)h);
}
if (c == 0) {
if (kDebugStringPoolNoisy) {
@@ -1030,11 +1129,15 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
// span tags; since those always appear at the end of the string
// block, start searching at the back.
for (int i=mHeader->stringCount-1; i>=0; i--) {
- const char16_t* s = stringAt(i, &len);
+ const base::expected<StringPiece16, NullOrIOError> s = stringAt(i);
+ if (UNLIKELY(IsIOError(s))) {
+ return base::unexpected(s.error());
+ }
if (kDebugStringPoolNoisy) {
- ALOGI("Looking at %s, i=%d\n", String8(s).string(), i);
+ ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).string(), i);
}
- if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) {
+ if (s.has_value() && strLen == s->size() &&
+ strzcmp16(s->data(), s->size(), str, strLen) == 0) {
if (kDebugStringPoolNoisy) {
ALOGI("MATCH!");
}
@@ -1043,8 +1146,7 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
}
}
}
-
- return NAME_NOT_FOUND;
+ return base::unexpected(std::nullopt);
}
size_t ResStringPool::size() const
@@ -1062,9 +1164,10 @@ size_t ResStringPool::bytes() const
return (mError == NO_ERROR) ? mHeader->header.size : 0;
}
-const void* ResStringPool::data() const
+incfs::map_ptr<void> ResStringPool::data() const
{
- return mHeader;
+
+ return mHeader.unsafe_ptr();
}
bool ResStringPool::isSorted() const
@@ -1121,7 +1224,7 @@ int32_t ResXMLParser::getCommentID() const
const char16_t* ResXMLParser::getComment(size_t* outLen) const
{
int32_t id = getCommentID();
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
uint32_t ResXMLParser::getLineNumber() const
@@ -1140,7 +1243,7 @@ int32_t ResXMLParser::getTextID() const
const char16_t* ResXMLParser::getText(size_t* outLen) const
{
int32_t id = getTextID();
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
ssize_t ResXMLParser::getTextValue(Res_value* outValue) const
@@ -1164,7 +1267,7 @@ const char16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const
{
int32_t id = getNamespacePrefixID();
//printf("prefix=%d event=%p\n", id, mEventCode);
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
int32_t ResXMLParser::getNamespaceUriID() const
@@ -1179,7 +1282,7 @@ const char16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const
{
int32_t id = getNamespaceUriID();
//printf("uri=%d event=%p\n", id, mEventCode);
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
int32_t ResXMLParser::getElementNamespaceID() const
@@ -1196,7 +1299,7 @@ int32_t ResXMLParser::getElementNamespaceID() const
const char16_t* ResXMLParser::getElementNamespace(size_t* outLen) const
{
int32_t id = getElementNamespaceID();
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
int32_t ResXMLParser::getElementNameID() const
@@ -1213,7 +1316,7 @@ int32_t ResXMLParser::getElementNameID() const
const char16_t* ResXMLParser::getElementName(size_t* outLen) const
{
int32_t id = getElementNameID();
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
size_t ResXMLParser::getAttributeCount() const
@@ -1246,7 +1349,7 @@ const char16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen)
if (kDebugXMLNoisy) {
printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
}
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const
@@ -1256,7 +1359,7 @@ const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) con
if (kDebugXMLNoisy) {
printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id);
}
- return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL;
}
int32_t ResXMLParser::getAttributeNameID(size_t idx) const
@@ -1281,7 +1384,7 @@ const char16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const
if (kDebugXMLNoisy) {
printf("getAttributeName 0x%zx=0x%x\n", idx, id);
}
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const
@@ -1291,7 +1394,7 @@ const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const
if (kDebugXMLNoisy) {
printf("getAttributeName 0x%zx=0x%x\n", idx, id);
}
- return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL;
}
uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const
@@ -1328,7 +1431,7 @@ const char16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen
if (kDebugXMLNoisy) {
printf("getAttributeValue 0x%zx=0x%x\n", idx, id);
}
- return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL;
+ return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL;
}
int32_t ResXMLParser::getAttributeDataType(size_t idx) const
@@ -3596,9 +3699,10 @@ struct ResTable::PackageGroup
ssize_t findType16(const char16_t* type, size_t len) const {
const size_t N = packages.size();
for (size_t i = 0; i < N; i++) {
- ssize_t index = packages[i]->typeStrings.indexOfString(type, len);
- if (index >= 0) {
- return index + packages[i]->typeIdOffset;
+ const base::expected<size_t, NullOrIOError> index =
+ packages[i]->typeStrings.indexOfString(type, len);
+ if (index.has_value()) {
+ return *index + packages[i]->typeIdOffset;
}
}
return -1;
@@ -4304,21 +4408,21 @@ bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* ou
outName->package = grp->name.string();
outName->packageLen = grp->name.size();
if (allowUtf8) {
- outName->type8 = entry.typeStr.string8(&outName->typeLen);
- outName->name8 = entry.keyStr.string8(&outName->nameLen);
+ outName->type8 = UnpackOptionalString(entry.typeStr.string8(), &outName->typeLen);
+ outName->name8 = UnpackOptionalString(entry.keyStr.string8(), &outName->nameLen);
} else {
outName->type8 = NULL;
outName->name8 = NULL;
}
if (outName->type8 == NULL) {
- outName->type = entry.typeStr.string16(&outName->typeLen);
+ outName->type = UnpackOptionalString(entry.typeStr.string16(), &outName->typeLen);
// If we have a bad index for some reason, we should abort.
if (outName->type == NULL) {
return false;
}
}
if (outName->name8 == NULL) {
- outName->name = entry.keyStr.string16(&outName->nameLen);
+ outName->name = UnpackOptionalString(entry.keyStr.string16(), &outName->nameLen);
// If we have a bad index for some reason, we should abort.
if (outName->name == NULL) {
return false;
@@ -4406,7 +4510,8 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
entry.package->header->index,
outValue->dataType,
outValue->dataType == Res_value::TYPE_STRING ?
- String8(entry.package->header->values.stringAt(outValue->data, &len)).string() :
+ String8(UnpackOptionalString(
+ entry.package->header->values.stringAt(outValue->data), &len)).string() :
"",
outValue->data);
}
@@ -4462,7 +4567,8 @@ const char16_t* ResTable::valueToString(
return NULL;
}
if (value->dataType == value->TYPE_STRING) {
- return getTableStringBlock(stringBlock)->stringAt(value->data, outLen);
+ return UnpackOptionalString(getTableStringBlock(stringBlock)->stringAt(value->data),
+ outLen);
}
// XXX do int to string conversions.
return NULL;
@@ -4978,15 +5084,13 @@ nope:
size_t targetTypeLen = typeLen;
do {
- ssize_t ti = group->packages[pi]->typeStrings.indexOfString(
- targetType, targetTypeLen);
- if (ti < 0) {
+ auto ti = group->packages[pi]->typeStrings.indexOfString(targetType, targetTypeLen);
+ if (!ti.has_value()) {
continue;
}
- ti += group->packages[pi]->typeIdOffset;
-
- const uint32_t identifier = findEntry(group, ti, name, nameLen,
+ *ti += group->packages[pi]->typeIdOffset;
+ const uint32_t identifier = findEntry(group, *ti, name, nameLen,
outTypeSpecFlags);
if (identifier != 0) {
if (fakePublic && outTypeSpecFlags) {
@@ -5009,8 +5113,9 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const
const size_t typeCount = typeList.size();
for (size_t i = 0; i < typeCount; i++) {
const Type* t = typeList[i];
- const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
- if (ei < 0) {
+ const base::expected<size_t, NullOrIOError> ei =
+ t->package->keyStrings.indexOfString(name, nameLen);
+ if (!ei.has_value()) {
continue;
}
@@ -5025,7 +5130,7 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const
continue;
}
- if (dtohl(entry->key.index) == (size_t) ei) {
+ if (dtohl(entry->key.index) == (size_t) *ei) {
uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index());
if (outTypeSpecFlags) {
Entry result;
@@ -6187,8 +6292,9 @@ void ResTable::forEachConfiguration(bool ignoreMipmap, bool ignoreAndroidPackage
for (size_t k = 0; k < numTypes; k++) {
const Type* type = typeList[k];
const ResStringPool& typeStrings = type->package->typeStrings;
- if (ignoreMipmap && typeStrings.string8ObjectAt(
- type->typeSpec->id - 1) == "mipmap") {
+ const base::expected<String8, NullOrIOError> typeStr = typeStrings.string8ObjectAt(
+ type->typeSpec->id - 1);
+ if (ignoreMipmap && typeStr.has_value() && *typeStr == "mipmap") {
continue;
}
@@ -6244,24 +6350,18 @@ void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales,
StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
: mPool(pool), mIndex(index) {}
-const char* StringPoolRef::string8(size_t* outLen) const {
- if (mPool != NULL) {
- return mPool->string8At(mIndex, outLen);
+base::expected<StringPiece, NullOrIOError> StringPoolRef::string8() const {
+ if (LIKELY(mPool != NULL)) {
+ return mPool->string8At(mIndex);
}
- if (outLen != NULL) {
- *outLen = 0;
- }
- return NULL;
+ return base::unexpected(std::nullopt);
}
-const char16_t* StringPoolRef::string16(size_t* outLen) const {
- if (mPool != NULL) {
- return mPool->stringAt(mIndex, outLen);
+base::expected<StringPiece16, NullOrIOError> StringPoolRef::string16() const {
+ if (LIKELY(mPool != NULL)) {
+ return mPool->stringAt(mIndex);
}
- if (outLen != NULL) {
- *outLen = 0;
- }
- return NULL;
+ return base::unexpected(std::nullopt);
}
bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const {
@@ -7380,13 +7480,13 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const
printf("(dynamic attribute) 0x%08x\n", value.data);
} else if (value.dataType == Res_value::TYPE_STRING) {
size_t len;
- const char* str8 = pkg->header->values.string8At(
- value.data, &len);
+ const char* str8 = UnpackOptionalString(pkg->header->values.string8At(
+ value.data), &len);
if (str8 != NULL) {
printf("(string8) \"%s\"\n", normalizeForOutput(str8).string());
} else {
- const char16_t* str16 = pkg->header->values.stringAt(
- value.data, &len);
+ const char16_t* str16 = UnpackOptionalString(pkg->header->values.stringAt(
+ value.data), &len);
if (str16 != NULL) {
printf("(string16) \"%s\"\n",
normalizeForOutput(String8(str16, len).string()).string());
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index c63dff8f9104..a34aa7239250 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -48,61 +48,76 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin
!(has_type_separator && out_type->empty());
}
-bool ToResourceName(const StringPoolRef& type_string_ref,
- const StringPoolRef& entry_string_ref,
- const StringPiece& package_name,
- AssetManager2::ResourceName* out_name) {
- out_name->package = package_name.data();
- out_name->package_len = package_name.size();
-
- out_name->type = type_string_ref.string8(&out_name->type_len);
- out_name->type16 = nullptr;
- if (out_name->type == nullptr) {
- out_name->type16 = type_string_ref.string16(&out_name->type_len);
- if (out_name->type16 == nullptr) {
- return false;
+base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
+ const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref,
+ const StringPiece& package_name) {
+ AssetManager2::ResourceName name{
+ .package = package_name.data(),
+ .package_len = package_name.size(),
+ };
+
+ if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8()) {
+ name.type = type_str->data();
+ name.type_len = type_str->size();
+ } else if (UNLIKELY(IsIOError(type_str))) {
+ return base::unexpected(type_str.error());
+ }
+
+ if (name.type == nullptr) {
+ if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16()) {
+ name.type16 = type16_str->data();
+ name.type_len = type16_str->size();
+ } else if (!type16_str.has_value()) {
+ return base::unexpected(type16_str.error());
}
}
- out_name->entry = entry_string_ref.string8(&out_name->entry_len);
- out_name->entry16 = nullptr;
- if (out_name->entry == nullptr) {
- out_name->entry16 = entry_string_ref.string16(&out_name->entry_len);
- if (out_name->entry16 == nullptr) {
- return false;
+ if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8()) {
+ name.entry = entry_str->data();
+ name.entry_len = entry_str->size();
+ } else if (UNLIKELY(IsIOError(entry_str))) {
+ return base::unexpected(entry_str.error());
+ }
+
+ if (name.entry == nullptr) {
+ if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16()) {
+ name.entry16 = entry16_str->data();
+ name.entry_len = entry16_str->size();
+ } else if (!entry16_str.has_value()) {
+ return base::unexpected(entry16_str.error());
}
}
- return true;
+ return name;
}
-std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name) {
+std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name) {
std::string result;
- if (resource_name->package != nullptr) {
- result.append(resource_name->package, resource_name->package_len);
+ if (resource_name.package != nullptr) {
+ result.append(resource_name.package, resource_name.package_len);
}
- if (resource_name->type != nullptr || resource_name->type16 != nullptr) {
+ if (resource_name.type != nullptr || resource_name.type16 != nullptr) {
if (!result.empty()) {
result += ":";
}
- if (resource_name->type != nullptr) {
- result.append(resource_name->type, resource_name->type_len);
+ if (resource_name.type != nullptr) {
+ result.append(resource_name.type, resource_name.type_len);
} else {
- result += util::Utf16ToUtf8(StringPiece16(resource_name->type16, resource_name->type_len));
+ result += util::Utf16ToUtf8(StringPiece16(resource_name.type16, resource_name.type_len));
}
}
- if (resource_name->entry != nullptr || resource_name->entry16 != nullptr) {
+ if (resource_name.entry != nullptr || resource_name.entry16 != nullptr) {
if (!result.empty()) {
result += "/";
}
- if (resource_name->entry != nullptr) {
- result.append(resource_name->entry, resource_name->entry_len);
+ if (resource_name.entry != nullptr) {
+ result.append(resource_name.entry, resource_name.entry_len);
} else {
- result += util::Utf16ToUtf8(StringPiece16(resource_name->entry16, resource_name->entry_len));
+ result += util::Utf16ToUtf8(StringPiece16(resource_name.entry16, resource_name.entry_len));
}
}
diff --git a/libs/androidfw/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp
index b39b5f0b8b36..1c5e5d44c845 100644
--- a/libs/androidfw/StreamingZipInflater.cpp
+++ b/libs/androidfw/StreamingZipInflater.cpp
@@ -70,13 +70,13 @@ StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart,
/*
* Streaming access to compressed data held in an mmapped region of memory
*/
-StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) {
+StreamingZipInflater::StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize) {
mFd = -1;
mDataMap = dataMap;
mOutTotalSize = uncompSize;
- mInTotalSize = dataMap->getDataLength();
+ mInTotalSize = dataMap->length();
- mInBuf = (uint8_t*) dataMap->getDataPtr();
+ mInBuf = (uint8_t*) dataMap->unsafe_data(); // IncFs safety handled in zlib.
mInBufSize = mInTotalSize;
mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE;
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index e77ac3df474c..52e7a70521a1 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -233,6 +233,29 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
}
/*
+ * Create a new incfs::IncFsFileMap object that spans the data in "entry".
+ */
+std::optional<incfs::IncFsFileMap> ZipFileRO::createEntryIncFsFileMap(ZipEntryRO entry) const
+{
+ const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
+ const ZipEntry& ze = zipEntry->entry;
+ int fd = GetFileDescriptor(mHandle);
+ size_t actualLen = 0;
+
+ if (ze.method == kCompressStored) {
+ actualLen = ze.uncompressed_length;
+ } else {
+ actualLen = ze.compressed_length;
+ }
+
+ incfs::IncFsFileMap newMap;
+ if (!newMap.Create(fd, ze.offset, actualLen, mFileName)) {
+ return std::nullopt;
+ }
+ return std::move(newMap);
+}
+
+/*
* Uncompress an entry, in its entirety, into the provided output buffer.
*
* This doesn't verify the data's CRC, which might be useful for
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 568e3b63d67f..58fc5bbbab5e 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -40,7 +40,7 @@ class FileReader : public zip_archive::Reader {
explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
}
- bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+ bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override {
// Data is usually requested sequentially, so this helps avoid pointless
// fseeks every time we perform a read. There's an impedence mismatch
// here because the original API was designed around pread and pwrite.
@@ -71,7 +71,7 @@ class FdReader : public zip_archive::Reader {
explicit FdReader(int fd) : mFd(fd) {
}
- bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+ bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override {
return android::base::ReadFullyAtOffset(mFd, buf, len, offset);
}
@@ -81,22 +81,27 @@ class FdReader : public zip_archive::Reader {
class BufferReader : public zip_archive::Reader {
public:
- BufferReader(const void* input, size_t inputSize) : Reader(),
- mInput(reinterpret_cast<const uint8_t*>(input)),
+ BufferReader(incfs::map_ptr<void> input, size_t inputSize) : Reader(),
+ mInput(input.convert<uint8_t>()),
mInputSize(inputSize) {
}
- bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const {
+ bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override {
if (mInputSize < len || offset > mInputSize - len) {
return false;
}
- memcpy(buf, mInput + offset, len);
+ const incfs::map_ptr<uint8_t> pos = mInput.offset(offset);
+ if (!pos.verify(len)) {
+ return false;
+ }
+
+ memcpy(buf, pos.unsafe_ptr(), len);
return true;
}
private:
- const uint8_t* mInput;
+ const incfs::map_ptr<uint8_t> mInput;
const size_t mInputSize;
};
@@ -138,7 +143,7 @@ class BufferWriter : public zip_archive::Writer {
return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0);
}
-/*static*/ bool ZipUtils::inflateToBuffer(const void* in, void* buf,
+/*static*/ bool ZipUtils::inflateToBuffer(incfs::map_ptr<void> in, void* buf,
long uncompressedLen, long compressedLen)
{
BufferReader reader(in, compressedLen);
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
index 96d44ab8e45c..5309ab2b6e20 100644
--- a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp
@@ -31,9 +31,6 @@ using android::LoadedArsc;
using android::StringPiece;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-
- std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(reinterpret_cast<const char*>(data), size));
-
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(data, size);
return 0;
} \ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 298509eb37a1..80bae20f3419 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -23,18 +23,18 @@
#include <stdio.h>
#include <sys/types.h>
-
#include <memory>
+#include <optional>
#include <android-base/unique_fd.h>
+#include <util/map_ptr.h>
+
#include <utils/Compat.h>
#include <utils/Errors.h>
#include <utils/String8.h>
namespace android {
-class FileMap;
-
/*
* Instances of this class provide read-only operations on a byte stream.
*
@@ -49,6 +49,8 @@ class FileMap;
class Asset {
public:
virtual ~Asset(void) = default;
+ Asset(const Asset& src) = delete;
+ Asset& operator=(const Asset& src) = delete;
static int32_t getGlobalCount();
static String8 getAssetAllocations();
@@ -87,8 +89,19 @@ public:
/*
* Get a pointer to a buffer with the entire contents of the file.
+ * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary.
+ *
+ * Use this function if the asset can never reside on IncFs.
*/
- virtual const void* getBuffer(bool wordAligned) = 0;
+ virtual const void* getBuffer(bool aligned) = 0;
+
+ /*
+ * Get a incfs::map_ptr<void> to a buffer with the entire contents of the file.
+ * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary.
+ *
+ * Use this function if the asset can potentially reside on IncFs.
+ */
+ virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned) = 0;
/*
* Get the total amount of data that can be read.
@@ -152,10 +165,6 @@ protected:
AccessMode getAccessMode(void) const { return mAccessMode; }
private:
- /* these operations are not implemented */
- Asset(const Asset& src);
- Asset& operator=(const Asset& src);
-
/* AssetManager needs access to our "create" functions */
friend class AssetManager;
friend class ApkAssets;
@@ -169,8 +178,7 @@ private:
/*
* Create the asset from a named, compressed file on disk (e.g. ".gz").
*/
- static Asset* createFromCompressedFile(const char* fileName,
- AccessMode mode);
+ static Asset* createFromCompressedFile(const char* fileName, AccessMode mode);
#if 0
/*
@@ -200,31 +208,21 @@ private:
/*
* Create the asset from a memory-mapped file segment.
*
- * The asset takes ownership of the FileMap.
- */
- static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode);
-
- /*
- * Create the asset from a memory-mapped file segment.
- *
- * The asset takes ownership of the FileMap and the file descriptor "fd". The file descriptor is
- * used to request new file descriptors using "openFileDescriptor".
+ * The asset takes ownership of the incfs::IncFsFileMap and the file descriptor "fd". The
+ * file descriptor is used to request new file descriptors using "openFileDescriptor".
*/
- static std::unique_ptr<Asset> createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
- base::unique_fd fd, AccessMode mode);
+ static std::unique_ptr<Asset> createFromUncompressedMap(incfs::IncFsFileMap&& dataMap,
+ AccessMode mode,
+ base::unique_fd fd = {});
/*
* Create the asset from a memory-mapped file segment with compressed
* data.
*
- * The asset takes ownership of the FileMap.
+ * The asset takes ownership of the incfs::IncFsFileMap.
*/
- static Asset* createFromCompressedMap(FileMap* dataMap,
- size_t uncompressedLen, AccessMode mode);
-
- static std::unique_ptr<Asset> createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
- size_t uncompressedLen, AccessMode mode);
-
+ static std::unique_ptr<Asset> createFromCompressedMap(incfs::IncFsFileMap&& dataMap,
+ size_t uncompressedLen, AccessMode mode);
/*
* Create from a reference-counted chunk of shared memory.
@@ -252,7 +250,7 @@ private:
class _FileAsset : public Asset {
public:
_FileAsset(void);
- virtual ~_FileAsset(void);
+ ~_FileAsset(void) override;
/*
* Use a piece of an already-open file.
@@ -266,21 +264,24 @@ public:
*
* On success, the object takes ownership of "dataMap" and "fd".
*/
- status_t openChunk(FileMap* dataMap, base::unique_fd fd);
+ status_t openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd);
/*
* Standard Asset interfaces.
*/
- virtual ssize_t read(void* buf, size_t count);
- virtual off64_t seek(off64_t offset, int whence);
- virtual void close(void);
- virtual const void* getBuffer(bool wordAligned);
- virtual off64_t getLength(void) const { return mLength; }
- virtual off64_t getRemainingLength(void) const { return mLength-mOffset; }
- virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const;
- virtual bool isAllocated(void) const { return mBuf != NULL; }
+ ssize_t read(void* buf, size_t count) override;
+ off64_t seek(off64_t offset, int whence) override;
+ void close(void) override;
+ const void* getBuffer(bool aligned) override;
+ incfs::map_ptr<void> getIncFsBuffer(bool aligned) override;
+ off64_t getLength(void) const override { return mLength; }
+ off64_t getRemainingLength(void) const override { return mLength-mOffset; }
+ int openFileDescriptor(off64_t* outStart, off64_t* outLength) const override;
+ bool isAllocated(void) const override { return mBuf != NULL; }
private:
+ incfs::map_ptr<void> ensureAlignment(const incfs::IncFsFileMap& map);
+
off64_t mStart; // absolute file offset of start of chunk
off64_t mLength; // length of the chunk
off64_t mOffset; // current local offset, 0 == mStart
@@ -295,10 +296,8 @@ private:
*/
enum { kReadVsMapThreshold = 4096 };
- FileMap* mMap; // for memory map
- unsigned char* mBuf; // for read
-
- const void* ensureAlignment(FileMap* map);
+ unsigned char* mBuf; // for read
+ std::optional<incfs::IncFsFileMap> mMap; // for memory map
};
@@ -323,7 +322,7 @@ public:
*
* On success, the object takes ownership of "fd".
*/
- status_t openChunk(FileMap* dataMap, size_t uncompressedLen);
+ status_t openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen);
/*
* Standard Asset interfaces.
@@ -331,24 +330,23 @@ public:
virtual ssize_t read(void* buf, size_t count);
virtual off64_t seek(off64_t offset, int whence);
virtual void close(void);
- virtual const void* getBuffer(bool wordAligned);
+ virtual const void* getBuffer(bool aligned);
+ virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned);
virtual off64_t getLength(void) const { return mUncompressedLen; }
virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; }
virtual int openFileDescriptor(off64_t* /* outStart */, off64_t* /* outLength */) const { return -1; }
virtual bool isAllocated(void) const { return mBuf != NULL; }
private:
- off64_t mStart; // offset to start of compressed data
- off64_t mCompressedLen; // length of the compressed data
- off64_t mUncompressedLen; // length of the uncompressed data
- off64_t mOffset; // current offset, 0 == start of uncomp data
-
- FileMap* mMap; // for memory-mapped input
- int mFd; // for file input
-
- class StreamingZipInflater* mZipInflater; // for streaming large compressed assets
-
- unsigned char* mBuf; // for getBuffer()
+ off64_t mStart; // offset to start of compressed data
+ off64_t mCompressedLen; // length of the compressed data
+ off64_t mUncompressedLen; // length of the uncompressed data
+ off64_t mOffset; // current offset, 0 == start of uncomp data
+ int mFd; // for file input
+
+ class StreamingZipInflater* mZipInflater; // for streaming large compressed assets
+ unsigned char* mBuf; // for getBuffer()
+ std::optional<incfs::IncFsFileMap> mMap; // for memory-mapped input
};
// need: shared mmap version?
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 30ef25c6a516..a92694c94b9f 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -131,8 +131,8 @@ class AssetManager2 {
bool GetOverlayablesToString(const android::StringPiece& package_name,
std::string* out) const;
- const std::unordered_map<std::string, std::string>*
- GetOverlayableMapForPackage(uint32_t package_id) const;
+ const std::unordered_map<std::string, std::string>* GetOverlayableMapForPackage(
+ uint32_t package_id) const;
// Returns whether the resources.arsc of any loaded apk assets is allocated in RAM (not mmapped).
bool ContainsAllocatedTable() const;
@@ -145,14 +145,16 @@ class AssetManager2 {
return configuration_;
}
- // Returns all configurations for which there are resources defined. This includes resource
- // configurations in all the ApkAssets set for this AssetManager.
+ // Returns all configurations for which there are resources defined, or an I/O error if reading
+ // resource data failed.
+ //
+ // This includes resource configurations in all the ApkAssets set for this AssetManager.
// If `exclude_system` is set to true, resource configurations from system APKs
// ('android' package, other libraries) will be excluded from the list.
// If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
// will be excluded from the list.
- std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false,
- bool exclude_mipmap = false) const;
+ base::expected<std::set<ResTable_config>, IOError> GetResourceConfigurations(
+ bool exclude_system = false, bool exclude_mipmap = false) const;
// Returns all the locales for which there are resources defined. This includes resource
// locales in all the ApkAssets set for this AssetManager.
@@ -194,77 +196,119 @@ class AssetManager2 {
std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
Asset::AccessMode mode) const;
- // Populates the `out_name` parameter with resource name information.
- // Utf8 strings are preferred, and only if they are unavailable are
- // the Utf16 variants populated.
- // Returns false if the resource was not found or the name was missing/corrupt.
- bool GetResourceName(uint32_t resid, ResourceName* out_name) const;
-
- // Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
- // See ResTable_config for the list of configuration axis.
- // Returns false if the resource was not found.
- bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) 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.
+ //
+ // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data
+ // failed.
+ base::expected<ResourceName, NullOrIOError> GetResourceName(uint32_t resid) const;
// Finds the resource ID assigned to `resource_name`.
+ //
// `resource_name` must be of the form '[package:][type/]entry'.
// If no package is specified in `resource_name`, then `fallback_package` is used as the package.
// If no type is specified in `resource_name`, then `fallback_type` is used as the type.
- // Returns 0x0 if no resource by that name was found.
- uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {},
- const std::string& fallback_package = {}) const;
-
- // Retrieves the best matching resource with ID `resid`. The resource value is filled into
- // `out_value` and the configuration for the selected value is populated in `out_selected_config`.
- // `out_flags` holds the same flags as retrieved with GetResourceFlags().
- // If `density_override` is non-zero, the configuration to match against is overridden with that
- // density.
//
- // Returns a valid cookie if the resource was found. If the resource was not found, or if the
- // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false,
- // this function logs if the resource was a map/bag type before returning kInvalidCookie.
- ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
- Res_value* out_value, ResTable_config* out_selected_config,
- uint32_t* out_flags) const;
-
- // Resolves the resource reference in `in_out_value` if the data type is
- // Res_value::TYPE_REFERENCE.
- // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`.
- // `in_out_value` is the reference to resolve. The result is placed back into this object.
- // `in_out_flags` is the type spec flags returned from calls to GetResource() or
- // GetResourceFlags(). Configuration flags of the values pointed to by the reference
- // are OR'd together with `in_out_flags`.
- // `in_out_config` is populated with the configuration for which the resolved value was defined.
- // `out_last_reference` is populated with the last reference ID before resolving to an actual
- // value. This is only initialized if the passed in `in_out_value` is a reference.
- // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
- // it was not found.
- ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
- ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
- uint32_t* out_last_reference) const;
+ // Returns a null error if no resource by that name was found, or an I/O error if reading resource
+ // data failed.
+ base::expected<uint32_t, NullOrIOError> GetResourceId(
+ const std::string& resource_name, const std::string& fallback_type = {},
+ const std::string& fallback_package = {}) const;
+
+ struct SelectedValue {
+ friend AssetManager2;
+ friend Theme;
+ SelectedValue() = default;
+ SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) :
+ cookie(entry.cookie), data(entry.value.data), type(entry.value.dataType),
+ flags(bag->type_spec_flags), resid(0U), config({}) {};
+
+ // The cookie representing the ApkAssets in which the value resides.
+ ApkAssetsCookie cookie = kInvalidCookie;
- // Resets the resource resolution structures in preparation for the next resource retrieval.
- void ResetResourceResolution() const;
+ // The data for this value, as interpreted according to `type`.
+ Res_value::data_type data;
- // Enables or disables resource resolution logging. Clears stored steps when disabled.
- void SetResourceResolutionLoggingEnabled(bool enabled);
+ // Type of the data value.
+ uint8_t type;
- // Returns formatted log of last resource resolution path, or empty if no resource has been
- // resolved yet.
- std::string GetLastResourceResolution() const;
+ // The bitmask of configuration axis that this resource varies with.
+ // See ResTable_config::CONFIG_*.
+ uint32_t flags;
+
+ // The resource ID from which this value was resolved.
+ uint32_t resid;
- const std::vector<uint32_t> GetBagResIdStack(uint32_t resid);
+ // The configuration for which the resolved value was defined.
+ ResTable_config config;
+
+ private:
+ SelectedValue(uint8_t value_type, Res_value::data_type value_data, ApkAssetsCookie cookie,
+ uint32_t type_flags, uint32_t resid, const ResTable_config& config) :
+ cookie(cookie), data(value_data), type(value_type), flags(type_flags),
+ resid(resid), config(config) {};
+ };
+
+ // Retrieves the best matching resource value with ID `resid`.
+ //
+ // If `may_be_bag` is false, this function logs if the resource was a map/bag type and returns a
+ // null result. If `density_override` is non-zero, the configuration to match against is
+ // overridden with that density.
+ //
+ // Returns a null error if a best match could not be found, or an I/O error if reading resource
+ // data failed.
+ base::expected<SelectedValue, NullOrIOError> GetResource(uint32_t resid, bool may_be_bag = false,
+ uint16_t density_override = 0U) const;
+
+ // Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE.
+ //
+ // If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the
+ // values pointed to by the reference are OR'd into `value.flags`. If `cache_value` is true, then
+ // the resolved value will be cached and used when attempting to resolve the resource id specified
+ // in `value`.
+ //
+ // Returns a null error if the resource could not be resolved, or an I/O error if reading
+ // resource data failed.
+ base::expected<std::monostate, NullOrIOError> ResolveReference(SelectedValue& value,
+ bool cache_value = false) const;
// Retrieves the best matching bag/map resource with ID `resid`.
+ //
// This method will resolve all parent references for this bag and merge keys with the child.
// To iterate over the keys, use the following idiom:
//
- // const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id);
- // if (bag != nullptr) {
- // for (auto iter = begin(bag); iter != end(bag); ++iter) {
+ // base::expected<const ResolvedBag*, NullOrIOError> bag = asset_manager->GetBag(id);
+ // if (bag.has_value()) {
+ // for (auto iter = begin(*bag); iter != end(*bag); ++iter) {
// ...
// }
// }
- const ResolvedBag* GetBag(uint32_t resid);
+ //
+ // Returns a null error if a best match could not be found, or an I/O error if reading resource
+ // data failed.
+ base::expected<const ResolvedBag*, NullOrIOError> GetBag(uint32_t resid) const;
+
+ // Retrieves the best matching bag/map resource of the resource referenced in `value`.
+ //
+ // If `value.type` is not Res_value::TYPE_REFERENCE, a null result is returned.
+ // Configuration flags of the bag pointed to by the reference are OR'd into `value.flags`.
+ //
+ // Returns a null error if a best match could not be found, or an I/O error if reading resource
+ // data failed.
+ base::expected<const ResolvedBag*, NullOrIOError> ResolveBag(SelectedValue& value) const;
+
+ const std::vector<uint32_t> GetBagResIdStack(uint32_t resid) const;
+
+ // Resets the resource resolution structures in preparation for the next resource retrieval.
+ void ResetResourceResolution() const;
+
+ // Enables or disables resource resolution logging. Clears stored steps when disabled.
+ void SetResourceResolutionLoggingEnabled(bool enabled);
+
+ // Returns formatted log of last resource resolution path, or empty if no resource has been
+ // resolved yet.
+ std::string GetLastResourceResolution() const;
// Creates a new Theme from this AssetManager.
std::unique_ptr<Theme> NewTheme();
@@ -286,11 +330,15 @@ class AssetManager2 {
private:
DISALLOW_COPY_AND_ASSIGN(AssetManager2);
+ struct TypeConfig {
+ incfs::verified_map_ptr<ResTable_type> type;
+ ResTable_config config;
+ };
+
// A collection of configurations and their associated ResTable_type that match the current
// AssetManager configuration.
struct FilteredConfigGroup {
- std::vector<ResTable_config> configurations;
- std::vector<const ResTable_type*> types;
+ std::vector<TypeConfig> type_configs;
};
// Represents an single package.
@@ -331,9 +379,7 @@ class AssetManager2 {
};
// Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
- // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
- // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
- // the ApkAssets in which the entry was found.
+ // Res_value, or a complex map/bag type. Returns a null result if a best entry cannot be found.
//
// `density_override` overrides the density of the current configuration when doing a search.
//
@@ -347,13 +393,15 @@ class AssetManager2 {
//
// NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
// bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
- ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
- bool ignore_configuration, FindEntryResult* out_entry) const;
+ base::expected<FindEntryResult, NullOrIOError> FindEntry(uint32_t resid,
+ uint16_t density_override,
+ bool stop_at_first_match,
+ bool ignore_configuration) const;
- ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx,
- uint16_t entry_idx, const ResTable_config& desired_config,
- bool /*stop_at_first_match*/,
- bool ignore_configuration, FindEntryResult* out_entry) const;
+ base::expected<FindEntryResult, NullOrIOError> FindEntryInternal(
+ const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx,
+ const ResTable_config& desired_config, bool stop_at_first_match,
+ bool ignore_configuration) const;
// Assigns package IDs to all shared library ApkAssets.
// Should be called whenever the ApkAssets are changed.
@@ -372,7 +420,8 @@ class AssetManager2 {
// AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
// been seen while traversing bag parents.
- const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
+ base::expected<const ResolvedBag*, NullOrIOError> GetBag(
+ uint32_t resid, std::vector<uint32_t>& child_resids) const;
// The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
// have a longer lifetime.
@@ -394,19 +443,20 @@ class AssetManager2 {
// Cached set of bags. These are cached because they can inherit keys from parent bags,
// which involves some calculation.
- std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
+ mutable std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
// Cached set of bag resid stacks for each bag. These are cached because they might be requested
// a number of times for each view during View inspection.
- std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_;
+ mutable std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_;
+
+ // Cached set of resolved resource values.
+ mutable std::unordered_map<uint32_t, SelectedValue> cached_resolved_values_;
// Whether or not to save resource resolution steps
bool resource_resolution_logging_enabled_ = false;
struct Resolution {
-
struct Step {
-
enum class Type {
INITIAL,
BETTER_MATCH,
@@ -455,55 +505,53 @@ class Theme {
public:
~Theme();
- // Applies the style identified by `resid` to this theme. This can be called
- // multiple times with different styles. By default, any theme attributes that
- // are already defined before this call are not overridden. If `force` is set
- // to true, this behavior is changed and all theme attributes from the style at
- // `resid` are applied.
- // Returns false if the style failed to apply.
- bool ApplyStyle(uint32_t resid, bool force = false);
+ // Applies the style identified by `resid` to this theme.
+ //
+ // This can be called multiple times with different styles. By default, any theme attributes that
+ // are already defined before this call are not overridden. If `force` is set to true, this
+ // behavior is changed and all theme attributes from the style at `resid` are applied.
+ //
+ // Returns a null error if the style could not be applied, or an I/O error if reading resource
+ // data failed.
+ base::expected<std::monostate, NullOrIOError> ApplyStyle(uint32_t resid, bool force = false);
- // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
- // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded
- // into both AssetManagers will be copied to this theme.
- void SetTo(const Theme& o);
+ // Sets this Theme to be a copy of `other` if `other` has the same AssetManager as this Theme.
+ //
+ // If `other` does not have the same AssetManager as this theme, only attributes from ApkAssets
+ // loaded into both AssetManagers will be copied to this theme.
+ //
+ // Returns an I/O error if reading resource data failed.
+ base::expected<std::monostate, IOError> SetTo(const Theme& other);
void Clear();
- void Dump() const;
+ // Retrieves the value of attribute ID `resid` in the theme.
+ //
+ // NOTE: This function does not do reference traversal. If you want to follow references to other
+ // resources to get the "real" value to use, you need to call ResolveReference() after this
+ // function.
+ std::optional<AssetManager2::SelectedValue> GetAttribute(uint32_t resid) const;
- inline const AssetManager2* GetAssetManager() const {
+ // This is like AssetManager2::ResolveReference(), but also takes care of resolving attribute
+ // references to the theme.
+ base::expected<std::monostate, NullOrIOError> ResolveAttributeReference(
+ AssetManager2::SelectedValue& value) const;
+
+ AssetManager2* GetAssetManager() {
return asset_manager_;
}
- inline AssetManager2* GetAssetManager() {
+ const AssetManager2* GetAssetManager() const {
return asset_manager_;
}
// Returns a bit mask of configuration changes that will impact this
// theme (and thus require completely reloading it).
- inline uint32_t GetChangingConfigurations() const {
+ uint32_t GetChangingConfigurations() const {
return type_spec_flags_;
}
- // Retrieve a value in the theme. If the theme defines this value, returns an asset cookie
- // indicating which ApkAssets it came from and populates `out_value` with the value.
- // `out_flags` is populated with a bitmask of the configuration axis with which the resource
- // varies.
- //
- // If the attribute is not found, returns kInvalidCookie.
- //
- // NOTE: This function does not do reference traversal. If you want to follow references to other
- // resources to get the "real" value to use, you need to call ResolveReference() after this
- // function.
- ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const;
-
- // This is like AssetManager2::ResolveReference(), but also takes
- // care of resolving attribute references to the theme.
- ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
- ResTable_config* in_out_selected_config = nullptr,
- uint32_t* in_out_type_spec_flags = nullptr,
- uint32_t* out_last_ref = nullptr) const;
+ void Dump() const;
private:
DISALLOW_COPY_AND_ASSIGN(Theme);
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
index d71aad29d917..1a69a309d365 100644
--- a/libs/androidfw/include/androidfw/AttributeResolution.h
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -45,20 +45,28 @@ enum {
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
-bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid,
- uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
+base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_style_attr,
+ uint32_t def_style_resid, uint32_t* src_values,
+ size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values,
+ uint32_t* out_indices);
// `out_values` must NOT be nullptr.
// `out_indices` is NOT optional and must NOT be nullptr.
-void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
- uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
- uint32_t* out_values, uint32_t* out_indices);
+base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* xml_parser,
+ uint32_t def_style_attr,
+ uint32_t def_style_resid,
+ const uint32_t* attrs, size_t attrs_length,
+ uint32_t* out_values, uint32_t* out_indices);
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
-bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
+base::expected<std::monostate, IOError> RetrieveAttributes(AssetManager2* assetmanager,
+ ResXMLParser* xml_parser,
+ uint32_t* attrs,
+ size_t attrs_length,
+ uint32_t* out_values,
+ uint32_t* out_indices);
} // namespace android
diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
index a0f23433c676..f1c43b298e53 100644
--- a/libs/androidfw/include/androidfw/Chunk.h
+++ b/libs/androidfw/include/androidfw/Chunk.h
@@ -36,7 +36,7 @@ namespace android {
// of the chunk.
class Chunk {
public:
- explicit Chunk(const ResChunk_header* chunk) : device_chunk_(chunk) {}
+ explicit Chunk(incfs::verified_map_ptr<ResChunk_header> chunk) : device_chunk_(chunk) {}
// Returns the type of the chunk. Caller need not worry about endianness.
inline int type() const { return dtohs(device_chunk_->type); }
@@ -49,21 +49,18 @@ class Chunk {
inline size_t header_size() const { return dtohs(device_chunk_->headerSize); }
template <typename T, size_t MinSize = sizeof(T)>
- inline const T* header() const {
- if (header_size() >= MinSize) {
- return reinterpret_cast<const T*>(device_chunk_);
- }
- return nullptr;
+ inline incfs::map_ptr<T> header() const {
+ return (header_size() >= MinSize) ? device_chunk_.convert<T>() : nullptr;
}
- inline const void* data_ptr() const {
- return reinterpret_cast<const uint8_t*>(device_chunk_) + header_size();
+ inline incfs::map_ptr<void> data_ptr() const {
+ return device_chunk_.offset(header_size());
}
inline size_t data_size() const { return size() - header_size(); }
private:
- const ResChunk_header* device_chunk_;
+ const incfs::verified_map_ptr<ResChunk_header> device_chunk_;
};
// Provides a Java style iterator over an array of ResChunk_header's.
@@ -84,11 +81,11 @@ class Chunk {
//
class ChunkIterator {
public:
- ChunkIterator(const void* data, size_t len)
- : next_chunk_(reinterpret_cast<const ResChunk_header*>(data)),
+ ChunkIterator(incfs::map_ptr<void> data, size_t len)
+ : next_chunk_(data.convert<ResChunk_header>()),
len_(len),
last_error_(nullptr) {
- CHECK(next_chunk_ != nullptr) << "data can't be nullptr";
+ CHECK((bool) next_chunk_) << "data can't be null";
if (len_ != 0) {
VerifyNextChunk();
}
@@ -113,7 +110,7 @@ class ChunkIterator {
// Returns false if there was an error. For legacy purposes.
bool VerifyNextChunkNonFatal();
- const ResChunk_header* next_chunk_;
+ incfs::map_ptr<ResChunk_header> next_chunk_;
size_t len_;
const char* last_error_;
bool last_error_was_fatal_ = true;
diff --git a/libs/androidfw/include/androidfw/Errors.h b/libs/androidfw/include/androidfw/Errors.h
new file mode 100644
index 000000000000..948162d10480
--- /dev/null
+++ b/libs/androidfw/include/androidfw/Errors.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROIDFW_ERRORS_H_
+#define ANDROIDFW_ERRORS_H_
+
+#include <optional>
+#include <variant>
+
+#include <android-base/result.h>
+
+namespace android {
+
+enum class IOError {
+ // Used when reading a file residing on an IncFs file-system times out.
+ PAGES_MISSING = -1,
+};
+
+// Represents an absent result or an I/O error.
+using NullOrIOError = std::variant<std::nullopt_t, IOError>;
+
+// Checks whether the result holds an unexpected I/O error.
+template <typename T>
+static inline bool IsIOError(const base::expected<T, NullOrIOError> result) {
+ return !result.has_value() && std::holds_alternative<IOError>(result.error());
+}
+
+static inline IOError GetIOError(const NullOrIOError& error) {
+ return std::get<IOError>(error);
+}
+
+} // namespace android
+
+#endif //ANDROIDFW_ERRORS_H_
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index ab0f47f025d2..fdab03ba2de4 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -40,8 +40,8 @@ class IdmapResMap;
class OverlayStringPool : public ResStringPool {
public:
virtual ~OverlayStringPool();
- const char16_t* stringAt(size_t idx, size_t* outLen) const override;
- const char* string8At(size_t idx, size_t* outLen) const override;
+ base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const override;
+ base::expected<StringPiece, NullOrIOError> string8At(size_t idx) const override;
size_t size() const override;
explicit OverlayStringPool(const LoadedIdmap* loaded_idmap);
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 89ff9f52125d..17d97a2a2e73 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -23,7 +23,8 @@
#include <unordered_map>
#include <unordered_set>
-#include "android-base/macros.h"
+#include <android-base/macros.h>
+#include <android-base/result.h>
#include "androidfw/ByteBucketArray.h"
#include "androidfw/Chunk.h"
@@ -49,7 +50,7 @@ struct TypeSpec {
// Pointer to the mmapped data where flags are kept.
// Flags denote whether the resource entry is public
// and under which configurations it varies.
- const ResTable_typeSpec* type_spec;
+ incfs::verified_map_ptr<ResTable_typeSpec> type_spec;
// The number of types that follow this struct.
// There is a type for each configuration that entries are defined for.
@@ -57,15 +58,17 @@ struct TypeSpec {
// Trick to easily access a variable number of Type structs
// proceeding this struct, and to ensure their alignment.
- const ResTable_type* types[0];
+ incfs::verified_map_ptr<ResTable_type> types[0];
- inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const {
+ base::expected<uint32_t, NullOrIOError> GetFlagsForEntryIndex(uint16_t entry_index) const {
if (entry_index >= dtohl(type_spec->entryCount)) {
- return 0u;
+ return 0U;
}
-
- const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1);
- return flags[entry_index];
+ const auto entry_flags_ptr = ((type_spec + 1).convert<uint32_t>() + entry_index);
+ if (!entry_flags_ptr) {
+ return base::unexpected(IOError::PAGES_MISSING);
+ }
+ return entry_flags_ptr.value();
}
};
@@ -161,13 +164,17 @@ class LoadedPackage {
// the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
// Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible
// for patching the correct package ID to the resource ID.
- uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const;
+ base::expected<uint32_t, NullOrIOError> FindEntryByName(const std::u16string& type_name,
+ const std::u16string& entry_name) const;
- static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index);
+ static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntry(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
- static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index);
+ static base::expected<uint32_t, NullOrIOError> GetEntryOffset(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
- static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset);
+ static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntryFromOffset(
+ incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
// Returns the string pool where type names are stored.
inline const ResStringPool* GetTypeStringPool() const {
@@ -220,7 +227,8 @@ class LoadedPackage {
// Populates a set of ResTable_config structs, possibly excluding configurations defined for
// the mipmap type.
- void CollectConfigurations(bool exclude_mipmap, std::set<ResTable_config>* out_configs) const;
+ base::expected<std::monostate, IOError> CollectConfigurations(
+ bool exclude_mipmap, std::set<ResTable_config>* out_configs) const;
// Populates a set of strings representing locales.
// If `canonicalize` is set to true, each locale is transformed into its canonical format
@@ -300,7 +308,8 @@ class LoadedArsc {
// If `load_as_shared_library` is set to true, the application package (0x7f) is treated
// as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an
// ID.
- static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data,
+ static std::unique_ptr<const LoadedArsc> Load(incfs::map_ptr<void> data,
+ size_t length,
const LoadedIdmap* loaded_idmap = nullptr,
package_property_t property_flags = 0U);
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 04ba78b6705d..fb5f86473189 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -20,7 +20,10 @@
#ifndef _LIBS_UTILS_RESOURCE_TYPES_H
#define _LIBS_UTILS_RESOURCE_TYPES_H
+#include <android-base/expected.h>
+
#include <androidfw/Asset.h>
+#include <androidfw/Errors.h>
#include <androidfw/LocaleData.h>
#include <androidfw/StringPiece.h>
#include <utils/Errors.h>
@@ -497,7 +500,7 @@ public:
virtual ~ResStringPool();
void setToEmpty();
- status_t setTo(const void* data, size_t size, bool copyData=false);
+ status_t setTo(incfs::map_ptr<void> data, size_t size, bool copyData=false);
status_t getError() const;
@@ -505,48 +508,49 @@ public:
// Return string entry as UTF16; if the pool is UTF8, the string will
// be converted before returning.
- inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const {
- return stringAt(ref.index, outLen);
+ inline base::expected<StringPiece16, NullOrIOError> stringAt(
+ const ResStringPool_ref& ref) const {
+ return stringAt(ref.index);
}
- virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
+ virtual base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const;
// Note: returns null if the string pool is not UTF8.
- virtual const char* string8At(size_t idx, size_t* outLen) const;
+ virtual base::expected<StringPiece, NullOrIOError> string8At(size_t idx) const;
// Return string whether the pool is UTF8 or UTF16. Does not allow you
// to distinguish null.
- const String8 string8ObjectAt(size_t idx) const;
+ base::expected<String8, IOError> string8ObjectAt(size_t idx) const;
- const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const;
- const ResStringPool_span* styleAt(size_t idx) const;
+ base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> styleAt(
+ const ResStringPool_ref& ref) const;
+ base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> styleAt(size_t idx) const;
- ssize_t indexOfString(const char16_t* str, size_t strLen) const;
+ base::expected<size_t, NullOrIOError> indexOfString(const char16_t* str, size_t strLen) const;
virtual size_t size() const;
size_t styleCount() const;
size_t bytes() const;
- const void* data() const;
-
+ incfs::map_ptr<void> data() const;
bool isSorted() const;
bool isUTF8() const;
private:
- status_t mError;
- void* mOwnedData;
- const ResStringPool_header* mHeader;
- size_t mSize;
- mutable Mutex mDecodeLock;
- const uint32_t* mEntries;
- const uint32_t* mEntryStyles;
- const void* mStrings;
- char16_t mutable** mCache;
- uint32_t mStringPoolSize; // number of uint16_t
- const uint32_t* mStyles;
- uint32_t mStylePoolSize; // number of uint32_t
-
- const char* stringDecodeAt(size_t idx, const uint8_t* str, const size_t encLen,
- size_t* outLen) const;
+ status_t mError;
+ void* mOwnedData;
+ incfs::verified_map_ptr<ResStringPool_header> mHeader;
+ size_t mSize;
+ mutable Mutex mDecodeLock;
+ incfs::map_ptr<uint32_t> mEntries;
+ incfs::map_ptr<uint32_t> mEntryStyles;
+ incfs::map_ptr<void> mStrings;
+ char16_t mutable** mCache;
+ uint32_t mStringPoolSize; // number of uint16_t
+ incfs::map_ptr<uint32_t> mStyles;
+ uint32_t mStylePoolSize; // number of uint32_t
+
+ base::expected<StringPiece, NullOrIOError> stringDecodeAt(
+ size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const;
};
/**
@@ -558,8 +562,8 @@ public:
StringPoolRef() = default;
StringPoolRef(const ResStringPool* pool, uint32_t index);
- const char* string8(size_t* outLen) const;
- const char16_t* string16(size_t* outLen) const;
+ base::expected<StringPiece, NullOrIOError> string8() const;
+ base::expected<StringPiece16, NullOrIOError> string16() const;
private:
const ResStringPool* mPool = nullptr;
@@ -1797,6 +1801,16 @@ private:
bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
+template<typename TChar, typename E>
+static const TChar* UnpackOptionalString(base::expected<BasicStringPiece<TChar>, E>&& result,
+ size_t* outLen) {
+ if (result.has_value()) {
+ *outLen = result->size();
+ return result->data();
+ }
+ return NULL;
+}
+
/**
* Convenience class for accessing data in a ResTable resource.
*/
diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h
index e649940cdde1..bd1c44033b88 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -30,13 +30,12 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin
// Convert a type_string_ref, entry_string_ref, and package to AssetManager2::ResourceName.
// Useful for getting resource name without re-running AssetManager2::FindEntry searches.
-bool ToResourceName(const StringPoolRef& type_string_ref,
- const StringPoolRef& entry_string_ref,
- const StringPiece& package_name,
- AssetManager2::ResourceName* out_name);
+base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
+ const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref,
+ const StringPiece& package_name);
// Formats a ResourceName to "package:type/entry_name".
-std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name);
+std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name);
inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) {
return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24);
diff --git a/libs/androidfw/include/androidfw/StreamingZipInflater.h b/libs/androidfw/include/androidfw/StreamingZipInflater.h
index 3ace5d5a83cf..472b794b911c 100644
--- a/libs/androidfw/include/androidfw/StreamingZipInflater.h
+++ b/libs/androidfw/include/androidfw/StreamingZipInflater.h
@@ -19,6 +19,8 @@
#include <unistd.h>
#include <inttypes.h>
+
+#include <util/map_ptr.h>
#include <zlib.h>
#include <utils/Compat.h>
@@ -34,7 +36,7 @@ public:
StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize);
// Flavor that gets the compressed data from an in-memory buffer
- StreamingZipInflater(class FileMap* dataMap, size_t uncompSize);
+ StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize);
~StreamingZipInflater();
@@ -54,7 +56,7 @@ private:
// where to find the uncompressed data
int mFd;
off64_t mInFileStart; // where the compressed data lives in the file
- class FileMap* mDataMap;
+ const incfs::IncFsFileMap* mDataMap;
z_stream mInflateState;
bool mStreamNeedsInit;
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index 9a3646b49db8..aceeeccccb61 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -22,7 +22,8 @@
#include <sstream>
#include <vector>
-#include "android-base/macros.h"
+#include <android-base/macros.h>
+#include <util/map_ptr.h>
#include "androidfw/StringPiece.h"
@@ -126,6 +127,11 @@ std::string Utf16ToUtf8(const StringPiece16& utf16);
std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
+template <typename T>
+bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
+ return ((size_t)data.unsafe_ptr() & 0x3U) == 0;
+}
+
} // namespace util
} // namespace android
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index c221e3b7aeae..10f6d0655bf4 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -30,17 +30,20 @@
#ifndef __LIBS_ZIPFILERO_H
#define __LIBS_ZIPFILERO_H
-#include <utils/Compat.h>
-#include <utils/Errors.h>
-#include <utils/FileMap.h>
-#include <utils/threads.h>
-
+#include <optional>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
+#include <util/map_ptr.h>
+
+#include <utils/Compat.h>
+#include <utils/Errors.h>
+#include <utils/FileMap.h>
+#include <utils/threads.h>
+
struct ZipArchive;
typedef ZipArchive* ZipArchiveHandle;
@@ -136,14 +139,26 @@ public:
uint32_t* pCrc32) const;
/*
- * Create a new FileMap object that maps a subset of the archive. For
+ * Create a new FileMap object that maps a subset of the archive. For
* an uncompressed entry this effectively provides a pointer to the
* actual data, for a compressed entry this provides the input buffer
* for inflate().
+ *
+ * Use this function if the archive can never reside on IncFs.
*/
FileMap* createEntryFileMap(ZipEntryRO entry) const;
/*
+ * Create a new incfs::IncFsFileMap object that maps a subset of the archive. For
+ * an uncompressed entry this effectively provides a pointer to the
+ * actual data, for a compressed entry this provides the input buffer
+ * for inflate().
+ *
+ * Use this function if the archive can potentially reside on IncFs.
+ */
+ std::optional<incfs::IncFsFileMap> createEntryIncFsFileMap(ZipEntryRO entry) const;
+
+ /*
* Uncompress the data into a buffer. Depending on the compression
* format, this is either an "inflate" operation or a memcpy.
*
diff --git a/libs/androidfw/include/androidfw/ZipUtils.h b/libs/androidfw/include/androidfw/ZipUtils.h
index 4d35e992cc89..dbfec34fda89 100644
--- a/libs/androidfw/include/androidfw/ZipUtils.h
+++ b/libs/androidfw/include/androidfw/ZipUtils.h
@@ -25,6 +25,8 @@
#include <stdio.h>
#include <time.h>
+#include "util/map_ptr.h"
+
namespace android {
/*
@@ -40,8 +42,8 @@ public:
long compressedLen);
static bool inflateToBuffer(int fd, void* buf, long uncompressedLen,
long compressedLen);
- static bool inflateToBuffer(const void *in, void* buf, long uncompressedLen,
- long compressedLen);
+ static bool inflateToBuffer(incfs::map_ptr<void> in, void* buf,
+ long uncompressedLen, long compressedLen);
/*
* Someday we might want to make this generic and handle bzip2 ".bz2"
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 437e14772964..c7ae618991b9 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -139,9 +139,13 @@ static void BM_AssetManagerGetBag(benchmark::State& state) {
assets.SetApkAssets({apk.get()});
while (state.KeepRunning()) {
- const ResolvedBag* bag = assets.GetBag(app::R::style::StyleTwo);
- const auto bag_end = end(bag);
- for (auto iter = begin(bag); iter != bag_end; ++iter) {
+ auto bag = assets.GetBag(app::R::style::StyleTwo);
+ if (!bag.has_value()) {
+ state.SkipWithError("Failed to load get bag");
+ return;
+ }
+ const auto bag_end = end(*bag);
+ for (auto iter = begin(*bag); iter != bag_end; ++iter) {
uint32_t key = iter->key;
Res_value value = iter->value;
benchmark::DoNotOptimize(key);
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 8c255d16fe1f..471b0ee1e7e9 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -108,24 +108,18 @@ TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
assetmanager.SetConfiguration(desired_config);
assetmanager.SetApkAssets({basic_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
// Came from our ApkAssets.
- EXPECT_EQ(0, cookie);
+ EXPECT_EQ(0, value->cookie);
// It is the default config.
- EXPECT_EQ(0, selected_config.language[0]);
- EXPECT_EQ(0, selected_config.language[1]);
+ EXPECT_EQ(0, value->config.language[0]);
+ EXPECT_EQ(0, value->config.language[1]);
// It is a string.
- EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+ EXPECT_EQ(Res_value::TYPE_STRING, value->type);
}
TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) {
@@ -138,24 +132,18 @@ TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) {
assetmanager.SetConfiguration(desired_config);
assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
// Came from our de_fr ApkAssets.
- EXPECT_EQ(1, cookie);
+ EXPECT_EQ(1, value->cookie);
// The configuration is German.
- EXPECT_EQ('d', selected_config.language[0]);
- EXPECT_EQ('e', selected_config.language[1]);
+ EXPECT_EQ('d', value->config.language[0]);
+ EXPECT_EQ('e', value->config.language[1]);
// It is a string.
- EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+ EXPECT_EQ(Res_value::TYPE_STRING, value->type);
}
TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) {
@@ -166,44 +154,35 @@ TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) {
assetmanager.SetApkAssets(
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(libclient::R::string::foo_one, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(libclient::R::string::foo_one);
+ ASSERT_TRUE(value.has_value());
// Reference comes from libclient.
- EXPECT_EQ(2, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+ EXPECT_EQ(2, value->cookie);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
// Lookup the reference.
- cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/,
- &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(1, cookie);
- EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+ value = assetmanager.GetResource(value->data);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(1, value->cookie);
+ EXPECT_EQ(Res_value::TYPE_STRING, value->type);
EXPECT_EQ(std::string("Foo from lib_one"),
- GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data));
+ GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data));
- cookie = assetmanager.GetResource(libclient::R::string::foo_two, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ value = assetmanager.GetResource(libclient::R::string::foo_two);
+ ASSERT_TRUE(value.has_value());
// Reference comes from libclient.
- EXPECT_EQ(2, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+ EXPECT_EQ(2, value->cookie);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
// Lookup the reference.
- cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/,
- &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(0, cookie);
- EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
+ value = assetmanager.GetResource(value->data);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(0, value->cookie);
+ EXPECT_EQ(Res_value::TYPE_STRING, value->type);
EXPECT_EQ(std::string("Foo from lib_two"),
- GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data));
+ GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data));
}
TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) {
@@ -211,16 +190,10 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) {
assetmanager.SetApkAssets({appaslib_assets_.get()});
// The appaslib package will have been assigned the package ID 0x02.
-
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
- ApkAssetsCookie cookie = assetmanager.GetResource(
- fix_package_id(appaslib::R::integer::number1, 0x02), false /*may_be_bag*/,
- 0u /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data);
+ auto value = assetmanager.GetResource(fix_package_id(appaslib::R::integer::number1, 0x02));
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value->data);
}
TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) {
@@ -238,40 +211,40 @@ TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) {
return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get());
};
- ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f);
- ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03);
- ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02);
+ ASSERT_EQ(0x7f, get_first_package_id(overlayable_assets_.get()));
+ ASSERT_EQ(0x03, get_first_package_id(overlay_assets_.get()));
+ ASSERT_EQ(0x02, get_first_package_id(lib_one_assets_.get()));
}
TEST_F(AssetManager2Test, GetSharedLibraryResourceName) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({lib_one_assets_.get()});
- AssetManager2::ResourceName name;
- ASSERT_TRUE(assetmanager.GetResourceName(lib_one::R::string::foo, &name));
- std::string formatted_name = ToFormattedResourceString(&name);
- ASSERT_EQ(formatted_name, "com.android.lib_one:string/foo");
+ auto name = assetmanager.GetResourceName(lib_one::R::string::foo);
+ ASSERT_TRUE(name.has_value());
+ ASSERT_EQ("com.android.lib_one:string/foo", ToFormattedResourceString(*name));
}
TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
- const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1);
- ASSERT_NE(nullptr, bag);
- ASSERT_EQ(3u, bag->entry_count);
+ auto bag = assetmanager.GetBag(basic::R::array::integerArray1);
+ ASSERT_TRUE(bag.has_value());
- EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[0].value.dataType);
- EXPECT_EQ(1u, bag->entries[0].value.data);
- EXPECT_EQ(0, bag->entries[0].cookie);
+ ASSERT_EQ(3u, (*bag)->entry_count);
- EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[1].value.dataType);
- EXPECT_EQ(2u, bag->entries[1].value.data);
- EXPECT_EQ(0, bag->entries[1].cookie);
+ EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[0].value.dataType);
+ EXPECT_EQ(1u, (*bag)->entries[0].value.data);
+ EXPECT_EQ(0, (*bag)->entries[0].cookie);
- EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[2].value.dataType);
- EXPECT_EQ(3u, bag->entries[2].value.data);
- EXPECT_EQ(0, bag->entries[2].cookie);
+ EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[1].value.dataType);
+ EXPECT_EQ(2u, (*bag)->entries[1].value.data);
+ EXPECT_EQ(0, (*bag)->entries[1].cookie);
+
+ EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[2].value.dataType);
+ EXPECT_EQ(3u, (*bag)->entries[2].value.data);
+ EXPECT_EQ(0, (*bag)->entries[2].cookie);
}
TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {}
@@ -284,15 +257,16 @@ TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) {
assetmanager.SetApkAssets(
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
- const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03));
- ASSERT_NE(nullptr, bag);
- ASSERT_GE(bag->entry_count, 2u);
+ auto bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03));
+ ASSERT_TRUE(bag.has_value());
+
+ ASSERT_GE((*bag)->entry_count, 2u);
// First two attributes come from lib_one.
- EXPECT_EQ(1, bag->entries[0].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[0].key));
- EXPECT_EQ(1, bag->entries[1].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+ EXPECT_EQ(1, (*bag)->entries[0].cookie);
+ EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key));
+ EXPECT_EQ(1, (*bag)->entries[1].cookie);
+ EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
}
TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) {
@@ -303,17 +277,17 @@ TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) {
assetmanager.SetApkAssets(
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
- const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib);
- ASSERT_NE(nullptr, bag);
- ASSERT_EQ(bag->entry_count, 2u);
+ auto bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib);
+ ASSERT_TRUE(bag.has_value());
+ ASSERT_EQ((*bag)->entry_count, 2u);
// First attribute comes from lib_two.
- EXPECT_EQ(2, bag->entries[0].cookie);
- EXPECT_EQ(0x02, get_package_id(bag->entries[0].key));
+ EXPECT_EQ(2, (*bag)->entries[0].cookie);
+ EXPECT_EQ(0x02, get_package_id((*bag)->entries[0].key));
// The next two attributes come from lib_one.
- EXPECT_EQ(2, bag->entries[1].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+ EXPECT_EQ(2, (*bag)->entries[1].cookie);
+ EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
}
TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
@@ -324,79 +298,79 @@ TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
assetmanager.SetApkAssets(
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
- const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme);
- ASSERT_NE(nullptr, bag);
- ASSERT_GE(bag->entry_count, 2u);
+ auto bag = assetmanager.GetBag(libclient::R::style::Theme);
+ ASSERT_TRUE(bag.has_value());
+ ASSERT_GE((*bag)->entry_count, 2u);
// First two attributes come from lib_one.
- EXPECT_EQ(1, bag->entries[0].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[0].key));
- EXPECT_EQ(1, bag->entries[1].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+ EXPECT_EQ(1, (*bag)->entries[0].cookie);
+ EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key));
+ EXPECT_EQ(1, (*bag)->entries[1].cookie);
+ EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key));
}
TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({style_assets_.get()});
- const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleOne);
- ASSERT_NE(nullptr, bag_one);
- ASSERT_EQ(2u, bag_one->entry_count);
+ auto bag_one = assetmanager.GetBag(app::R::style::StyleOne);
+ ASSERT_TRUE(bag_one.has_value());
+ ASSERT_EQ(2u, (*bag_one)->entry_count);
- EXPECT_EQ(app::R::attr::attr_one, bag_one->entries[0].key);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[0].value.dataType);
- EXPECT_EQ(1u, bag_one->entries[0].value.data);
- EXPECT_EQ(0, bag_one->entries[0].cookie);
+ EXPECT_EQ(app::R::attr::attr_one, (*bag_one)->entries[0].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[0].value.dataType);
+ EXPECT_EQ(1u, (*bag_one)->entries[0].value.data);
+ EXPECT_EQ(0, (*bag_one)->entries[0].cookie);
- EXPECT_EQ(app::R::attr::attr_two, bag_one->entries[1].key);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[1].value.dataType);
- EXPECT_EQ(2u, bag_one->entries[1].value.data);
- EXPECT_EQ(0, bag_one->entries[1].cookie);
+ EXPECT_EQ(app::R::attr::attr_two, (*bag_one)->entries[1].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[1].value.dataType);
+ EXPECT_EQ(2u, (*bag_one)->entries[1].value.data);
+ EXPECT_EQ(0, (*bag_one)->entries[1].cookie);
- const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
- ASSERT_NE(nullptr, bag_two);
- ASSERT_EQ(6u, bag_two->entry_count);
+ auto bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
+ ASSERT_TRUE(bag_two.has_value());
+ ASSERT_EQ(6u, (*bag_two)->entry_count);
// attr_one is inherited from StyleOne.
- EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[0].value.dataType);
- EXPECT_EQ(1u, bag_two->entries[0].value.data);
- EXPECT_EQ(0, bag_two->entries[0].cookie);
- EXPECT_EQ(app::R::style::StyleOne, bag_two->entries[0].style);
+ EXPECT_EQ(app::R::attr::attr_one, (*bag_two)->entries[0].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[0].value.dataType);
+ EXPECT_EQ(1u, (*bag_two)->entries[0].value.data);
+ EXPECT_EQ(0, (*bag_two)->entries[0].cookie);
+ EXPECT_EQ(app::R::style::StyleOne, (*bag_two)->entries[0].style);
// attr_two should be overridden from StyleOne by StyleTwo.
- EXPECT_EQ(app::R::attr::attr_two, bag_two->entries[1].key);
- EXPECT_EQ(Res_value::TYPE_STRING, bag_two->entries[1].value.dataType);
- EXPECT_EQ(0, bag_two->entries[1].cookie);
- EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[1].style);
+ EXPECT_EQ(app::R::attr::attr_two, (*bag_two)->entries[1].key);
+ EXPECT_EQ(Res_value::TYPE_STRING, (*bag_two)->entries[1].value.dataType);
+ EXPECT_EQ(0, (*bag_two)->entries[1].cookie);
+ EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[1].style);
EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0),
- bag_two->entries[1].value.data));
+ (*bag_two)->entries[1].value.data));
// The rest are new attributes.
- EXPECT_EQ(app::R::attr::attr_three, bag_two->entries[2].key);
- EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, bag_two->entries[2].value.dataType);
- EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[2].value.data);
- EXPECT_EQ(0, bag_two->entries[2].cookie);
- EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[2].style);
-
- EXPECT_EQ(app::R::attr::attr_five, bag_two->entries[3].key);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, bag_two->entries[3].value.dataType);
- EXPECT_EQ(app::R::string::string_one, bag_two->entries[3].value.data);
- EXPECT_EQ(0, bag_two->entries[3].cookie);
- EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[3].style);
-
- EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[4].key);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType);
- EXPECT_EQ(3u, bag_two->entries[4].value.data);
- EXPECT_EQ(0, bag_two->entries[4].cookie);
- EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[4].style);
-
- EXPECT_EQ(app::R::attr::attr_empty, bag_two->entries[5].key);
- EXPECT_EQ(Res_value::TYPE_NULL, bag_two->entries[5].value.dataType);
- EXPECT_EQ(Res_value::DATA_NULL_EMPTY, bag_two->entries[5].value.data);
- EXPECT_EQ(0, bag_two->entries[5].cookie);
- EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[5].style);
+ EXPECT_EQ(app::R::attr::attr_three, (*bag_two)->entries[2].key);
+ EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, (*bag_two)->entries[2].value.dataType);
+ EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[2].value.data);
+ EXPECT_EQ(0, (*bag_two)->entries[2].cookie);
+ EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[2].style);
+
+ EXPECT_EQ(app::R::attr::attr_five, (*bag_two)->entries[3].key);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, (*bag_two)->entries[3].value.dataType);
+ EXPECT_EQ(app::R::string::string_one, (*bag_two)->entries[3].value.data);
+ EXPECT_EQ(0, (*bag_two)->entries[3].cookie);
+ EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[3].style);
+
+ EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[4].key);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[4].value.dataType);
+ EXPECT_EQ(3u, (*bag_two)->entries[4].value.data);
+ EXPECT_EQ(0, (*bag_two)->entries[4].cookie);
+ EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[4].style);
+
+ EXPECT_EQ(app::R::attr::attr_empty, (*bag_two)->entries[5].key);
+ EXPECT_EQ(Res_value::TYPE_NULL, (*bag_two)->entries[5].value.dataType);
+ EXPECT_EQ(Res_value::DATA_NULL_EMPTY, (*bag_two)->entries[5].value.data);
+ EXPECT_EQ(0, (*bag_two)->entries[5].cookie);
+ EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[5].style);
}
TEST_F(AssetManager2Test, MergeStylesCircularDependency) {
@@ -405,55 +379,41 @@ TEST_F(AssetManager2Test, MergeStylesCircularDependency) {
// GetBag should stop traversing the parents of styles when a circular
// dependency is detected
- const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleFour);
- ASSERT_NE(nullptr, bag_one);
- ASSERT_EQ(3u, bag_one->entry_count);
+ auto bag = assetmanager.GetBag(app::R::style::StyleFour);
+ ASSERT_TRUE(bag.has_value());
+ ASSERT_EQ(3u, (*bag)->entry_count);
}
TEST_F(AssetManager2Test, ResolveReferenceToResource) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::integer::ref1, false /*may_be_bag*/,
- 0u /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
-
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(basic::R::integer::ref2, value.data);
+ auto value = assetmanager.GetResource(basic::R::integer::ref1);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(basic::R::integer::ref2, value->data);
- uint32_t last_ref = 0u;
- cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(12000u, value.data);
- EXPECT_EQ(basic::R::integer::ref2, last_ref);
+ auto result = assetmanager.ResolveReference(*value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(12000u, value->data);
+ EXPECT_EQ(basic::R::integer::ref2, value->resid);
}
TEST_F(AssetManager2Test, ResolveReferenceToBag) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/,
- 0u /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(basic::R::array::integerArray1, value->data);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(basic::R::array::integerArray1, value.data);
-
- uint32_t last_ref = 0u;
- cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(basic::R::array::integerArray1, value.data);
- EXPECT_EQ(basic::R::array::integerArray1, last_ref);
+ auto result = assetmanager.ResolveReference(*value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(basic::R::array::integerArray1, value->data);
+ EXPECT_EQ(basic::R::array::integerArray1, value->resid);
}
TEST_F(AssetManager2Test, ResolveDeepIdReference) {
@@ -461,50 +421,107 @@ TEST_F(AssetManager2Test, ResolveDeepIdReference) {
assetmanager.SetApkAssets({basic_assets_.get()});
// Set up the resource ids
- const uint32_t high_ref = assetmanager
- .GetResourceId("@id/high_ref", "values", "com.android.basic");
- ASSERT_NE(high_ref, 0u);
- const uint32_t middle_ref = assetmanager
- .GetResourceId("@id/middle_ref", "values", "com.android.basic");
- ASSERT_NE(middle_ref, 0u);
- const uint32_t low_ref = assetmanager
- .GetResourceId("@id/low_ref", "values", "com.android.basic");
- ASSERT_NE(low_ref, 0u);
+ auto high_ref = assetmanager.GetResourceId("@id/high_ref", "values", "com.android.basic");
+ ASSERT_TRUE(high_ref.has_value());
+
+ auto middle_ref = assetmanager.GetResourceId("@id/middle_ref", "values", "com.android.basic");
+ ASSERT_TRUE(middle_ref.has_value());
+
+ auto low_ref = assetmanager.GetResourceId("@id/low_ref", "values", "com.android.basic");
+ ASSERT_TRUE(low_ref.has_value());
// Retrieve the most shallow resource
- Res_value value;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = assetmanager.GetResource(high_ref, false /*may_be_bag*/,
- 0 /*density_override*/,
- &value, &config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(middle_ref, value.data);
+ auto value = assetmanager.GetResource(*high_ref);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(*middle_ref, value->data);;
// Check that resolving the reference resolves to the deepest id
- uint32_t last_ref = high_ref;
- assetmanager.ResolveReference(cookie, &value, &config, &flags, &last_ref);
- EXPECT_EQ(last_ref, low_ref);
+ auto result = assetmanager.ResolveReference(*value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(*low_ref, value->resid);
}
TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
- ResTable_config selected_config;
- memset(&selected_config, 0, sizeof(selected_config));
+ // Create some kind of value that is NOT a reference.
+ AssetManager2::SelectedValue value{};
+ value.cookie = 1;
+ value.type = Res_value::TYPE_STRING;
+ value.resid = basic::R::string::test1;
- uint32_t flags = 0u;
+ auto result = assetmanager.ResolveReference(value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(1, value.cookie);
+ EXPECT_EQ(basic::R::string::test1, value.resid);
+}
- // Create some kind of Res_value that is NOT a reference.
- Res_value value;
- value.dataType = Res_value::TYPE_STRING;
- value.data = 0;
+TEST_F(AssetManager2Test, ResolveReferenceMissingResourceDoNotCacheFlags) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({basic_assets_.get()});
+ {
+ AssetManager2::SelectedValue value{};
+ value.data = basic::R::string::test1;
+ value.type = Res_value::TYPE_REFERENCE;
+ value.flags = ResTable_config::CONFIG_KEYBOARD;
+
+ auto result = assetmanager.ResolveReference(value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_STRING, value.type);
+ EXPECT_EQ(0, value.cookie);
+ EXPECT_EQ(basic::R::string::test1, value.resid);
+ EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_KEYBOARD, value.flags);
+ }
+ {
+ AssetManager2::SelectedValue value{};
+ value.data = basic::R::string::test1;
+ value.type = Res_value::TYPE_REFERENCE;
+ value.flags = ResTable_config::CONFIG_COLOR_MODE;
+
+ auto result = assetmanager.ResolveReference(value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_STRING, value.type);
+ EXPECT_EQ(0, value.cookie);
+ EXPECT_EQ(basic::R::string::test1, value.resid);
+ EXPECT_EQ(ResTable_typeSpec::SPEC_PUBLIC | ResTable_config::CONFIG_COLOR_MODE, value.flags);
+ }
+}
+
+TEST_F(AssetManager2Test, ResolveReferenceMissingResource) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({basic_assets_.get()});
- uint32_t last_ref = basic::R::string::test1;
- EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref));
- EXPECT_EQ(basic::R::string::test1, last_ref);
+ const uint32_t kMissingResId = 0x8001ffff;
+ AssetManager2::SelectedValue value{};
+ value.type = Res_value::TYPE_REFERENCE;
+ value.data = kMissingResId;
+
+ auto result = assetmanager.ResolveReference(value);
+ ASSERT_FALSE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value.type);
+ EXPECT_EQ(kMissingResId, value.data);
+ EXPECT_EQ(kMissingResId, value.resid);
+ EXPECT_EQ(-1, value.cookie);
+ EXPECT_EQ(0, value.flags);
+}
+
+TEST_F(AssetManager2Test, ResolveReferenceMissingResourceLib) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({libclient_assets_.get()});
+
+ AssetManager2::SelectedValue value{};
+ value.type = Res_value::TYPE_REFERENCE;
+ value.data = libclient::R::string::foo_one;
+
+ auto result = assetmanager.ResolveReference(value);
+ ASSERT_TRUE(result.has_value());
+ EXPECT_EQ(Res_value::TYPE_DYNAMIC_REFERENCE, value.type);
+ EXPECT_EQ(lib_one::R::string::foo, value.data);
+ EXPECT_EQ(libclient::R::string::foo_one, value.resid);
+ EXPECT_EQ(0, value.cookie);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value.flags);
}
static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations,
@@ -516,43 +533,45 @@ TEST_F(AssetManager2Test, GetResourceConfigurations) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()});
- std::set<ResTable_config> configurations = assetmanager.GetResourceConfigurations();
+ auto configurations = assetmanager.GetResourceConfigurations();
+ ASSERT_TRUE(configurations.has_value());
// We expect the locale sv from the system assets, and de and fr from basic_de_fr assets.
// And one extra for the default configuration.
- EXPECT_EQ(4u, configurations.size());
+ EXPECT_EQ(4u, configurations->size());
ResTable_config expected_config;
memset(&expected_config, 0, sizeof(expected_config));
expected_config.language[0] = 's';
expected_config.language[1] = 'v';
- EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
expected_config.language[0] = 'd';
expected_config.language[1] = 'e';
- EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
expected_config.language[0] = 'f';
expected_config.language[1] = 'r';
- EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
// Take out the system assets.
configurations = assetmanager.GetResourceConfigurations(true /* exclude_system */);
+ ASSERT_TRUE(configurations.has_value());
// We expect de and fr from basic_de_fr assets.
- EXPECT_EQ(2u, configurations.size());
+ EXPECT_EQ(2u, configurations->size());
expected_config.language[0] = 's';
expected_config.language[1] = 'v';
- EXPECT_FALSE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_FALSE(IsConfigurationPresent(*configurations, expected_config));
expected_config.language[0] = 'd';
expected_config.language[1] = 'e';
- EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
expected_config.language[0] = 'f';
expected_config.language[1] = 'r';
- EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+ EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config));
}
TEST_F(AssetManager2Test, GetResourceLocales) {
@@ -578,12 +597,17 @@ TEST_F(AssetManager2Test, GetResourceId) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_.get()});
- EXPECT_EQ(basic::R::layout::main,
- assetmanager.GetResourceId("com.android.basic:layout/main", "", ""));
- EXPECT_EQ(basic::R::layout::main,
- assetmanager.GetResourceId("layout/main", "", "com.android.basic"));
- EXPECT_EQ(basic::R::layout::main,
- assetmanager.GetResourceId("main", "layout", "com.android.basic"));
+ auto resid = assetmanager.GetResourceId("com.android.basic:layout/main", "", "");
+ ASSERT_TRUE(resid.has_value());
+ EXPECT_EQ(basic::R::layout::main, *resid);
+
+ resid = assetmanager.GetResourceId("layout/main", "", "com.android.basic");
+ ASSERT_TRUE(resid.has_value());
+ EXPECT_EQ(basic::R::layout::main, *resid);
+
+ resid = assetmanager.GetResourceId("main", "layout", "com.android.basic");
+ ASSERT_TRUE(resid.has_value());
+ EXPECT_EQ(basic::R::layout::main, *resid);
}
TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {
@@ -658,14 +682,8 @@ TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) {
assetmanager.SetApkAssets({basic_assets_.get()});
assetmanager.SetResourceResolutionLoggingEnabled(false);
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
auto result = assetmanager.GetLastResourceResolution();
EXPECT_EQ("", result);
@@ -693,17 +711,12 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) {
assetmanager.SetConfiguration(desired_config);
assetmanager.SetApkAssets({basic_assets_.get()});
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
auto result = assetmanager.GetLastResourceResolution();
- EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic", result);
+ EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
+ "\tFor config -de\n\tFound initial: com.android.basic", result);
}
TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
@@ -717,17 +730,14 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
assetmanager.SetConfiguration(desired_config);
assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()});
- Res_value value = Res_value();
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
auto result = assetmanager.GetLastResourceResolution();
- EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic\n\tFound better: com.android.basic -de", result);
+ EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n"
+ "\tFor config -de\n"
+ "\tFound initial: com.android.basic\n"
+ "\tFound better: com.android.basic -de", result);
}
TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
@@ -739,14 +749,8 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
assetmanager.SetConfiguration(desired_config);
assetmanager.SetApkAssets({basic_assets_.get()});
- Res_value value = Res_value();
- ResTable_config selected_config;
- uint32_t flags;
-
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/,
- 0 /*density_override*/, &value, &selected_config, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
+ auto value = assetmanager.GetResource(basic::R::string::test1);
+ ASSERT_TRUE(value.has_value());
auto resultEnabled = assetmanager.GetLastResourceResolution();
ASSERT_NE("", resultEnabled);
diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp
index fa300c50218a..ddd8ab820cb1 100644
--- a/libs/androidfw/tests/AttributeResolution_bench.cpp
+++ b/libs/androidfw/tests/AttributeResolution_bench.cpp
@@ -108,27 +108,20 @@ static void BM_ApplyStyleFramework(benchmark::State& state) {
device_config.screenHeightDp = 1024;
device_config.sdkVersion = 27;
- Res_value value;
- ResTable_config config;
- uint32_t flags = 0u;
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/,
- 0u /*density_override*/, &value, &config, &flags);
- if (cookie == kInvalidCookie) {
+ auto value = assetmanager.GetResource(basic::R::layout::layoutt);
+ if (!value.has_value()) {
state.SkipWithError("failed to find R.layout.layout");
return;
}
- size_t len = 0u;
- const char* layout_path =
- assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len);
- if (layout_path == nullptr || len == 0u) {
+ auto layout_path = assetmanager.GetStringPoolForCookie(value->cookie)->string8At(value->data);
+ if (!layout_path.has_value()) {
state.SkipWithError("failed to lookup layout path");
return;
}
- std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(
- StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER);
+ std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(layout_path->to_string(), value->cookie,
+ Asset::ACCESS_BUFFER);
if (asset == nullptr) {
state.SkipWithError("failed to load layout");
return;
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index 24361b5817f4..bb9129ad01c8 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -77,9 +77,9 @@ TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) {
{fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
std::array<uint32_t, attrs.size() + 1> indices;
- ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/,
- fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(),
- indices.data());
+ ASSERT_TRUE(ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/,
+ fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(),
+ values.data(), indices.data()).has_value());
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
@@ -102,7 +102,7 @@ TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) {
TEST_F(AttributeResolutionTest, Theme) {
std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
+ ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value());
std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_empty}};
@@ -110,7 +110,7 @@ TEST_F(AttributeResolutionTest, Theme) {
ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/,
nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(),
- attrs.size(), values.data(), nullptr /*out_indices*/));
+ attrs.size(), values.data(), nullptr /*out_indices*/).has_value());
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
@@ -162,7 +162,7 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) {
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(),
- values.data(), nullptr /*out_indices*/));
+ values.data(), nullptr /*out_indices*/).has_value());
uint32_t* values_cursor = values.data();
EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
@@ -207,15 +207,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) {
TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
+ ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value());
std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
std::array<uint32_t, attrs.size() + 1> indices;
- ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(),
- attrs.size(), values.data(), indices.data());
+ ASSERT_TRUE(ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/,
+ attrs.data(), attrs.size(), values.data(), indices.data()).has_value());
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index faddfe599af4..0fa0573bcbb8 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -71,15 +71,9 @@ void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_
assetmanager.SetConfiguration(*config);
}
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
- uint32_t last_id = 0u;
-
while (state.KeepRunning()) {
- ApkAssetsCookie cookie = assetmanager.GetResource(
- resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags);
- assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id);
+ auto value = assetmanager.GetResource(resid);
+ assetmanager.ResolveReference(*value);
}
}
diff --git a/libs/androidfw/tests/CommonHelpers.cpp b/libs/androidfw/tests/CommonHelpers.cpp
index faa5350f9ecc..3396729536a4 100644
--- a/libs/androidfw/tests/CommonHelpers.cpp
+++ b/libs/androidfw/tests/CommonHelpers.cpp
@@ -58,8 +58,9 @@ const std::string& GetTestDataPath() {
}
std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) {
- String8 str = pool->string8ObjectAt(idx);
- return std::string(str.string(), str.length());
+ auto str = pool->string8ObjectAt(idx);
+ CHECK(str.has_value()) << "failed to find string entry";
+ return std::string(str->string(), str->length());
}
} // namespace android
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 7aa0dbbafab3..3f0c7cbc8ffc 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -62,10 +62,10 @@ class IdmapTest : public ::testing::Test {
std::unique_ptr<const ApkAssets> overlayable_assets_;
};
-std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value,
- ApkAssetsCookie cookie) {
+std::string GetStringFromApkAssets(const AssetManager2& asset_manager,
+ const AssetManager2::SelectedValue& value) {
auto assets = asset_manager.GetApkAssets();
- const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool();
+ const ResStringPool* string_pool = assets[value.cookie]->GetLoadedArsc()->GetStringPool();
return GetStringFromPool(string_pool, value.data);
}
@@ -75,117 +75,88 @@ TEST_F(IdmapTest, OverlayOverridesResourceValue) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
- ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One");
+
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable5);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+ ASSERT_EQ("Overlay One", GetStringFromApkAssets(asset_manager, *value));
}
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 0U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
- ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes");
+
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable10);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 0U);
+ ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+ ASSERT_EQ("yes", GetStringFromApkAssets(asset_manager, *value));
}
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
- ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24));
+
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable8);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(value->data, (overlay::R::string::internal & 0x00ffffffU) | (0x02U << 24));
}
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC);
- ASSERT_EQ(val.data, 42);
+
+ auto value = asset_manager.GetResource(overlayable::R::integer::config_integer);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_INT_DEC);
+ ASSERT_EQ(value->data, 42);
}
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
-
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
- ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string");
+
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+ ASSERT_EQ("Hardcoded string", GetStringFromApkAssets(asset_manager, *value));
}
TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
- ASSERT_EQ(val.data, overlayable::R::string::overlayable7);
+
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable9);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(value->data, overlayable::R::string::overlayable7);
}
TEST_F(IdmapTest, OverlayOverridesXmlParser) {
AssetManager2 asset_manager;
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- ASSERT_EQ(cookie, 2U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
- ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml");
-
- auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie,
+
+ auto value = asset_manager.GetResource(overlayable::R::layout::hello_view);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(value->cookie, 2U);
+ ASSERT_EQ(value->type, Res_value::TYPE_STRING);
+ ASSERT_EQ("res/layout/hello_view.xml", GetStringFromApkAssets(asset_manager, *value));
+
+ auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", value->cookie,
Asset::ACCESS_RANDOM);
- auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie);
+ auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(value->cookie);
auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table));
status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false);
ASSERT_EQ(err, NO_ERROR);
@@ -216,32 +187,24 @@ TEST_F(IdmapTest, OverlaidResourceHasSameName) {
asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
overlay_assets_.get()});
- AssetManager2::ResourceName name;
- ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name));
- ASSERT_EQ(std::string(name.package), "com.android.overlayable");
- ASSERT_EQ(String16(name.type16), u"string");
- ASSERT_EQ(std::string(name.entry), "overlayable9");
+ auto name = asset_manager.GetResourceName(overlayable::R::string::overlayable9);
+ ASSERT_TRUE(name.has_value());
+ ASSERT_EQ("com.android.overlayable", std::string(name->package));
+ ASSERT_EQ(std::u16string(u"string"), std::u16string(name->type16));
+ ASSERT_EQ("overlayable9", std::string(name->entry));
}
TEST_F(IdmapTest, OverlayLoaderInterop) {
- std::string contents;
auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER);
-
AssetManager2 asset_manager;
asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
overlay_assets_.get()});
- Res_value val;
- ResTable_config config;
- uint32_t flags;
- ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
- false /* may_be_bag */,
- 0 /* density_override */, &val, &config,
- &flags);
- std::cout << asset_manager.GetLastResourceResolution();
- ASSERT_EQ(cookie, 1U);
- ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
- ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader");
+ auto value = asset_manager.GetResource(overlayable::R::string::overlayable11);
+ ASSERT_TRUE(value.has_value());
+ ASSERT_EQ(1U, value->cookie);
+ ASSERT_EQ(Res_value::TYPE_STRING, value->type);
+ ASSERT_EQ("loader", GetStringFromApkAssets(asset_manager, *value));
}
TEST_F(IdmapTest, OverlayAssetsIsUpToDate) {
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 2d69dfe4f429..63574110a817 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -50,7 +50,8 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) {
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc",
&contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ auto loaded_arsc = LoadedArsc::Load(reinterpret_cast<const void*>(contents.data()),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
@@ -66,9 +67,8 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) {
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- const ResTable_type* type = type_spec->types[0];
- ASSERT_THAT(type, NotNull());
- ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+ auto type = type_spec->types[0];
+ ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
}
TEST(LoadedArscTest, LoadSparseEntryApp) {
@@ -76,7 +76,8 @@ TEST(LoadedArscTest, LoadSparseEntryApp) {
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
&contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
@@ -90,9 +91,8 @@ TEST(LoadedArscTest, LoadSparseEntryApp) {
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- const ResTable_type* type = type_spec->types[0];
- ASSERT_THAT(type, NotNull());
- ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+ auto type = type_spec->types[0];
+ ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
}
TEST(LoadedArscTest, LoadSharedLibrary) {
@@ -100,7 +100,8 @@ TEST(LoadedArscTest, LoadSharedLibrary) {
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
&contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
@@ -120,7 +121,8 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) {
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
@@ -145,8 +147,10 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC);
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length(),
+ nullptr /* loaded_idmap */,
+ PROPERTY_DYNAMIC);
ASSERT_THAT(loaded_arsc, NotNull());
const auto& packages = loaded_arsc->GetPackages();
@@ -159,7 +163,8 @@ TEST(LoadedArscTest, LoadFeatureSplit) {
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
&contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
@@ -172,15 +177,12 @@ TEST(LoadedArscTest, LoadFeatureSplit) {
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
- size_t len;
- const char16_t* type_name16 =
- package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len);
- ASSERT_THAT(type_name16, NotNull());
- EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string"));
+ auto type_name16 = package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1);
+ ASSERT_TRUE(type_name16.has_value());
+ EXPECT_THAT(util::Utf16ToUtf8(*type_name16), StrEq("string"));
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull());
+ ASSERT_TRUE(LoadedPackage::GetEntry(type_spec->types[0], entry_index).has_value());
}
// AAPT(2) generates resource tables with chunks in a certain order. The rule is that
@@ -205,7 +207,8 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) {
ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
@@ -215,12 +218,10 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) {
const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0);
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
type_spec = package->GetTypeSpecByTypeIndex(1);
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
}
TEST(LoadedArscTest, LoadOverlayable) {
@@ -228,7 +229,8 @@ TEST(LoadedArscTest, LoadOverlayable) {
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package = loaded_arsc->GetPackageById(
@@ -272,7 +274,8 @@ TEST(LoadedArscTest, ResourceIdentifierIterator) {
ASSERT_TRUE(
ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_NE(nullptr, loaded_arsc);
const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
@@ -320,7 +323,8 @@ TEST(LoadedArscTest, GetOverlayableMap) {
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
"resources.arsc", &contents));
- std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
ASSERT_NE(nullptr, loaded_arsc);
const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
@@ -345,7 +349,7 @@ TEST(LoadedArscTest, LoadCustomLoader) {
asset->getLength());
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(data, nullptr, PROPERTY_LOADER);
+ LoadedArsc::Load(data.data(), data.length(), nullptr, PROPERTY_LOADER);
ASSERT_THAT(loaded_arsc, NotNull());
const LoadedPackage* package =
@@ -361,9 +365,8 @@ TEST(LoadedArscTest, LoadCustomLoader) {
ASSERT_THAT(type_spec, NotNull());
ASSERT_THAT(type_spec->type_count, Ge(1u));
- const ResTable_type* type = type_spec->types[0];
- ASSERT_THAT(type, NotNull());
- ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+ auto type = type_spec->types[0];
+ ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value());
}
// structs with size fields (like Res_value, ResTable_entry) should be
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 326474e16e5d..9aeb00c47e63 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -442,22 +442,22 @@ TEST(ResTableTest, TruncatedEncodeLength) {
ASSERT_LT(val.data, pool->size());
// Make sure a string with a truncated length is read to its correct length
- size_t str_len;
- const char* target_str8 = pool->string8At(val.data, &str_len);
- ASSERT_TRUE(target_str8 != NULL);
- ASSERT_EQ(size_t(40076), String8(target_str8, str_len).size());
- ASSERT_EQ(target_str8[40075], ']');
+ auto target_str8 = pool->string8At(val.data);
+ ASSERT_TRUE(target_str8.has_value());
+ ASSERT_EQ(size_t(40076), String8(target_str8->data(), target_str8->size()).size());
+ ASSERT_EQ(target_str8->data()[40075], ']');
- const char16_t* target_str16 = pool->stringAt(val.data, &str_len);
- ASSERT_TRUE(target_str16 != NULL);
- ASSERT_EQ(size_t(40076), String16(target_str16, str_len).size());
- ASSERT_EQ(target_str8[40075], (char16_t) ']');
+ auto target_str16 = pool->stringAt(val.data);
+ ASSERT_TRUE(target_str16.has_value());
+ ASSERT_EQ(size_t(40076), String16(target_str16->data(), target_str16->size()).size());
+ ASSERT_EQ(target_str8->data()[40075], (char16_t) ']');
// Load an edited apk with the null terminator removed from the end of the
// string
std::string invalid_contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/length_decode/length_decode_invalid.apk",
- "resources.arsc", &invalid_contents));
+ ASSERT_TRUE(ReadFileFromZipToString(
+ GetTestDataPath() + "/length_decode/length_decode_invalid.apk", "resources.arsc",
+ &invalid_contents));
ResTable invalid_table;
ASSERT_EQ(NO_ERROR, invalid_table.add(invalid_contents.data(), invalid_contents.size()));
@@ -472,8 +472,8 @@ TEST(ResTableTest, TruncatedEncodeLength) {
// Make sure a string with a truncated length that is not null terminated errors
// and does not return the string
- ASSERT_TRUE(invalid_pool->string8At(invalid_val.data, &str_len) == NULL);
- ASSERT_TRUE(invalid_pool->stringAt(invalid_val.data, &str_len) == NULL);
+ ASSERT_FALSE(invalid_pool->string8At(invalid_val.data).has_value());
+ ASSERT_FALSE(invalid_pool->stringAt(invalid_val.data).has_value());
}
} // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
index a81bb6ffab06..10c0a4fc8316 100644
--- a/libs/androidfw/tests/TestHelpers.cpp
+++ b/libs/androidfw/tests/TestHelpers.cpp
@@ -73,11 +73,15 @@ AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
return AssertionFailure() << "table has no string pool for block " << block;
}
- const String8 actual_str = pool->string8ObjectAt(val.data);
- if (String8(expected_str) != actual_str) {
- return AssertionFailure() << actual_str.string();
+ auto actual_str = pool->string8ObjectAt(val.data);
+ if (!actual_str.has_value()) {
+ return AssertionFailure() << "could not find string entry";
}
- return AssertionSuccess() << actual_str.string();
+
+ if (String8(expected_str) != *actual_str) {
+ return AssertionFailure() << actual_str->string();
+ }
+ return AssertionSuccess() << actual_str->string();
}
} // namespace android
diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp
index 594c39eb682f..f3d60bbe4f15 100644
--- a/libs/androidfw/tests/Theme_bench.cpp
+++ b/libs/androidfw/tests/Theme_bench.cpp
@@ -70,11 +70,8 @@ static void BM_ThemeGetAttribute(benchmark::State& state) {
auto theme = assets.NewTheme();
theme->ApplyStyle(kStyleId, false /* force */);
- Res_value value;
- uint32_t flags;
-
while (state.KeepRunning()) {
- theme->GetAttribute(kAttrId, &value, &flags);
+ theme->GetAttribute(kAttrId);
}
}
BENCHMARK(BM_ThemeGetAttribute);
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 16b9c75982fb..f658735da515 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -67,10 +67,7 @@ TEST_F(ThemeTest, EmptyTheme) {
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
EXPECT_EQ(0u, theme->GetChangingConfigurations());
EXPECT_EQ(&assetmanager, theme->GetAssetManager());
-
- Res_value value;
- uint32_t flags;
- EXPECT_EQ(kInvalidCookie, theme->GetAttribute(app::R::attr::attr_one, &value, &flags));
+ EXPECT_FALSE(theme->GetAttribute(app::R::attr::attr_one).has_value());
}
TEST_F(ThemeTest, SingleThemeNoParent) {
@@ -78,23 +75,19 @@ TEST_F(ThemeTest, SingleThemeNoParent) {
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne));
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
-
- cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(1u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
-
- cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(2u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne).has_value());
+
+ auto value = theme->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(1u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
+
+ value = theme->GetAttribute(app::R::attr::attr_two);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(2u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
}
TEST_F(ThemeTest, SingleThemeWithParent) {
@@ -102,32 +95,28 @@ TEST_F(ThemeTest, SingleThemeWithParent) {
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
-
- cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(1u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
-
- cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_STRING, value.dataType);
- EXPECT_EQ(0, cookie);
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
+
+ auto value = theme->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(1u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
+
+ value = theme->GetAttribute(app::R::attr::attr_two);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_STRING, value->type);
+ EXPECT_EQ(0, value->cookie);
EXPECT_EQ(std::string("string"),
- GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value.data));
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value->data));
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// This attribute should point to an attr_indirect, so the result should be 3.
- cookie = theme->GetAttribute(app::R::attr::attr_three, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(3u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_three);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(3u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
}
TEST_F(ThemeTest, TryToUseBadResourceId) {
@@ -135,11 +124,8 @@ TEST_F(ThemeTest, TryToUseBadResourceId) {
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
-
- Res_value value;
- uint32_t flags;
- ASSERT_EQ(kInvalidCookie, theme->GetAttribute(0x7f000001, &value, &flags));
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
+ ASSERT_FALSE(theme->GetAttribute(0x7f000001));
}
TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) {
@@ -147,33 +133,29 @@ TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) {
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree));
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree).has_value());
// attr_one is still here from the base.
- cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(1u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ auto value = theme->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(1u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// check for the new attr_six
- cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(6u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_six);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(6u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// check for the old attr_five (force=true was not used).
- cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
- EXPECT_EQ(app::R::string::string_one, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_five);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
+ EXPECT_EQ(app::R::string::string_one, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
}
TEST_F(ThemeTest, MultipleThemesOverlaidForced) {
@@ -181,33 +163,29 @@ TEST_F(ThemeTest, MultipleThemesOverlaidForced) {
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
- ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */));
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value());
+ ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */).has_value());
// attr_one is still here from the base.
- cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(1u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ auto value = theme->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(1u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// check for the new attr_six
- cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(6u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_six);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(6u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// check for the new attr_five (force=true was used).
- cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(5u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme->GetAttribute(app::R::attr::attr_five);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(5u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
}
TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) {
@@ -216,28 +194,24 @@ TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) {
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/));
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
+ ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/).has_value());
// The attribute should be resolved to the final value.
- cookie = theme->GetAttribute(libclient::R::attr::foo, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(700u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ auto value = theme->GetAttribute(libclient::R::attr::foo);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(700u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// The reference should be resolved to a TYPE_REFERENCE.
- cookie = theme->GetAttribute(libclient::R::attr::bar, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+ value = theme->GetAttribute(libclient::R::attr::bar);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type);
// lib_one is assigned package ID 0x03.
- EXPECT_EQ(3u, get_package_id(value.data));
- EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value.data));
- EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value.data));
+ EXPECT_EQ(3u, get_package_id(value->data));
+ EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value->data));
+ EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value->data));
}
TEST_F(ThemeTest, CopyThemeSameAssetManager) {
@@ -245,24 +219,20 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) {
assetmanager.SetApkAssets({style_assets_.get()});
std::unique_ptr<Theme> theme_one = assetmanager.NewTheme();
- ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie;
+ ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne).has_value());
// attr_one is still here from the base.
- cookie = theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(1u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ auto value = theme_one->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(1u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
// attr_six is not here.
- EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags));
+ ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_six).has_value());
std::unique_ptr<Theme> theme_two = assetmanager.NewTheme();
- ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree));
+ ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree).has_value());
// Copy the theme to theme_one.
theme_one->SetTo(*theme_two);
@@ -271,14 +241,14 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) {
theme_two->Clear();
// attr_one is now not here.
- EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+ ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_one).has_value());
// attr_six is now here because it was copied.
- cookie = theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags);
- ASSERT_NE(kInvalidCookie, cookie);
- EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
- EXPECT_EQ(6u, value.data);
- EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
+ value = theme_one->GetAttribute(app::R::attr::attr_six);
+ ASSERT_TRUE(value);
+ EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type);
+ EXPECT_EQ(6u, value->data);
+ EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags);
}
TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) {
@@ -291,39 +261,43 @@ TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) {
style_assets_.get()});
auto theme_dst = assetmanager_dst.NewTheme();
- ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne));
+ ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne).has_value());
auto theme_src = assetmanager_src.NewTheme();
- ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One));
- ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo));
+ ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One).has_value());
+ ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo).has_value());
ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03),
- false /*force*/));
+ false /*force*/).has_value());
ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02),
- false /*force*/));
+ false /*force*/).has_value());
theme_dst->SetTo(*theme_src);
- Res_value value;
- uint32_t flags;
-
// System resources (present in destination asset manager).
- EXPECT_EQ(0, theme_dst->GetAttribute(R::attr::foreground, &value, &flags));
+ auto value = theme_dst->GetAttribute(R::attr::foreground);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(0, value->cookie);
// The cookie of the style asset is 3 in the source and 2 in the destination.
// Check that the cookie has been rewritten to the destination values.
- EXPECT_EQ(2, theme_dst->GetAttribute(app::R::attr::attr_one, &value, &flags));
+ value = theme_dst->GetAttribute(app::R::attr::attr_one);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(2, value->cookie);
// The cookie of the lib_one asset is 2 in the source and 1 in the destination.
// The package id of the lib_one package is 0x03 in the source and 0x02 in the destination
// Check that the cookie and packages have been rewritten to the destination values.
- EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value,
- &flags));
- EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value,
- &flags));
+ value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02));
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(1, value->cookie);
+
+ value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02));
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(1, value->cookie);
// attr2 references an attribute in lib_one. Check that the resolution of the attribute value is
// correct after the value of attr2 had its package id rewritten to the destination package id.
- EXPECT_EQ(700, value.data);
+ EXPECT_EQ(700, value->data);
}
TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) {
@@ -335,28 +309,32 @@ TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) {
auto theme_dst = assetmanager_dst.NewTheme();
auto theme_src = assetmanager_src.NewTheme();
- ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven));
+ ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven).has_value());
theme_dst->SetTo(*theme_src);
- Res_value value;
- uint32_t flags;
-
// Allow inline resource values to be copied even if the source apk asset is not present in the
// destination.
- EXPECT_EQ(0, theme_dst->GetAttribute(0x0101021b /* android:versionCode */, &value, &flags));
+ auto value = theme_dst->GetAttribute(0x0101021b /* android:versionCode */);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(0, value->cookie);
// Do not copy strings since the data is an index into the values string pool of the source apk
// asset.
- EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010001 /* android:label */, &value, &flags));
+ EXPECT_FALSE(theme_dst->GetAttribute(0x01010001 /* android:label */).has_value());
// Do not copy values that reference another resource if the resource is not present in the
// destination.
- EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010002 /* android:icon */, &value, &flags));
- EXPECT_EQ(-1, theme_dst->GetAttribute(0x010100d1 /* android:tag */, &value, &flags));
+ EXPECT_FALSE(theme_dst->GetAttribute(0x01010002 /* android:icon */).has_value());
+ EXPECT_FALSE(theme_dst->GetAttribute(0x010100d1 /* android:tag */).has_value());
// Allow @empty to and @null to be copied.
- EXPECT_EQ(0, theme_dst->GetAttribute(0x010100d0 /* android:id */, &value, &flags));
- EXPECT_EQ(0, theme_dst->GetAttribute(0x01010000 /* android:theme */, &value, &flags));
+ value = theme_dst->GetAttribute(0x010100d0 /* android:id */);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(0, value->cookie);
+
+ value = theme_dst->GetAttribute(0x01010000 /* android:theme */);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_EQ(0, value->cookie);
}
} // namespace android
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 1dbce58fb7c9..9a4ed8166de6 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -160,32 +160,20 @@ void SkiaCanvas::restoreToCount(int restoreCount) {
}
}
-static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) {
- SkCanvas::SaveLayerFlags layerFlags = 0;
-
- if (!(flags & SaveFlags::ClipToLayer)) {
- layerFlags |= SkCanvasPriv::kDontClipToLayer_SaveLayerFlag;
- }
-
- return layerFlags;
-}
-
-int SkiaCanvas::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
- SaveFlags::Flags flags) {
+int SkiaCanvas::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint) {
const SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
- const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags));
+ const SkCanvas::SaveLayerRec rec(&bounds, paint);
return mCanvas->saveLayer(rec);
}
-int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
- SaveFlags::Flags flags) {
+int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom, int alpha) {
if (static_cast<unsigned>(alpha) < 0xFF) {
SkPaint alphaPaint;
alphaPaint.setAlpha(alpha);
- return this->saveLayer(left, top, right, bottom, &alphaPaint, flags);
+ return this->saveLayer(left, top, right, bottom, &alphaPaint);
}
- return this->saveLayer(left, top, right, bottom, nullptr, flags);
+ return this->saveLayer(left, top, right, bottom, nullptr);
}
int SkiaCanvas::saveUnclippedLayer(int left, int top, int right, int bottom) {
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 2cb850c83934..591ae5c44227 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -73,10 +73,8 @@ public:
virtual void restoreToCount(int saveCount) override;
virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) override;
- virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
- SaveFlags::Flags flags) override;
- virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
- SaveFlags::Flags flags) 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;
virtual int saveUnclippedLayer(int left, int top, int right, int bottom) override;
virtual void getMatrix(SkMatrix* outMatrix) const override;
diff --git a/libs/hwui/canvas/CanvasOpBuffer.h b/libs/hwui/canvas/CanvasOpBuffer.h
index b80faeb1a65b..07e079a7d57f 100644
--- a/libs/hwui/canvas/CanvasOpBuffer.h
+++ b/libs/hwui/canvas/CanvasOpBuffer.h
@@ -46,6 +46,10 @@ public:
const SkMatrix& transform() const { return mTransform; }
CanvasOp<T>* operator->() noexcept { return &mImpl; }
+ const CanvasOp<T>* operator->() const noexcept { return &mImpl; }
+
+ CanvasOp<T>& op() noexcept { return mImpl; }
+ const CanvasOp<T>& op() const noexcept { return mImpl; }
};
extern template class OpBuffer<CanvasOpType, CanvasOpContainer>;
diff --git a/libs/hwui/canvas/CanvasOpRasterizer.cpp b/libs/hwui/canvas/CanvasOpRasterizer.cpp
index 97c418a3e8d0..25129f641c00 100644
--- a/libs/hwui/canvas/CanvasOpRasterizer.cpp
+++ b/libs/hwui/canvas/CanvasOpRasterizer.cpp
@@ -32,7 +32,7 @@ void rasterizeCanvasBuffer(const CanvasOpBuffer& source, SkCanvas* destination)
std::vector<SkMatrix> globalMatrixStack;
SkMatrix& currentGlobalTransform = globalMatrixStack.emplace_back(SkMatrix::I());
- source.for_each([&]<CanvasOpType T>(CanvasOpContainer<T> * op) {
+ source.for_each([&]<CanvasOpType T>(const CanvasOpContainer<T> * op) {
if constexpr (T == CanvasOpType::BeginZ || T == CanvasOpType::EndZ) {
// Do beginZ or endZ
LOG_ALWAYS_FATAL("TODO");
diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h
index efcb3739ab5d..f9df2f7aa5ba 100644
--- a/libs/hwui/canvas/CanvasOpTypes.h
+++ b/libs/hwui/canvas/CanvasOpTypes.h
@@ -50,7 +50,11 @@ enum class CanvasOpType : int8_t {
DrawPath,
DrawLine,
DrawVertices,
-
+ DrawImage,
+ DrawImageRect,
+ // DrawImageLattice also used to draw 9 patches
+ DrawImageLattice,
+ DrawPicture,
// TODO: Rest
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index 6a7653958981..8c7113d5d075 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -21,12 +21,16 @@
#include <SkPath.h>
#include <SkRegion.h>
#include <SkVertices.h>
+#include <SkImage.h>
+#include <SkPicture.h>
+#include <hwui/Bitmap.h>
#include <log/log.h>
#include "CanvasProperty.h"
#include "CanvasOpTypes.h"
#include <experimental/type_traits>
+#include <utility>
namespace android::uirenderer {
@@ -269,6 +273,97 @@ struct CanvasOp<CanvasOpType::DrawVertices> {
ASSERT_DRAWABLE()
};
+template<>
+struct CanvasOp<CanvasOpType::DrawImage> {
+
+ CanvasOp<CanvasOpType::DrawImageRect>(
+ const sk_sp<Bitmap>& bitmap,
+ float left,
+ float top,
+ SkPaint paint
+ ) : left(left),
+ top(top),
+ paint(std::move(paint)),
+ bitmap(bitmap),
+ image(bitmap->makeImage()) { }
+
+ float left;
+ float top;
+ SkPaint paint;
+ sk_sp<Bitmap> bitmap;
+ sk_sp<SkImage> image;
+
+ void draw(SkCanvas* canvas) const {
+ canvas->drawImage(image, left, top, &paint);
+ }
+ ASSERT_DRAWABLE()
+};
+
+template<>
+struct CanvasOp<CanvasOpType::DrawImageRect> {
+
+ CanvasOp<CanvasOpType::DrawImageRect>(
+ const sk_sp<Bitmap>& bitmap,
+ SkRect src,
+ SkRect dst,
+ SkPaint paint
+ ) : src(src),
+ dst(dst),
+ paint(std::move(paint)),
+ bitmap(bitmap),
+ image(bitmap->makeImage()) { }
+
+ SkRect src;
+ SkRect dst;
+ SkPaint paint;
+ sk_sp<Bitmap> bitmap;
+ sk_sp<SkImage> image;
+
+ void draw(SkCanvas* canvas) const {
+ canvas->drawImageRect(image,
+ src,
+ dst,
+ &paint,
+ SkCanvas::kFast_SrcRectConstraint
+ );
+ }
+ ASSERT_DRAWABLE()
+};
+
+template<>
+struct CanvasOp<CanvasOpType::DrawImageLattice> {
+
+ CanvasOp<CanvasOpType::DrawImageLattice>(
+ const sk_sp<Bitmap>& bitmap,
+ SkRect dst,
+ SkCanvas::Lattice lattice,
+ SkPaint paint
+ ): dst(dst),
+ lattice(lattice),
+ bitmap(bitmap),
+ image(bitmap->makeImage()),
+ paint(std::move(paint)) {}
+
+ SkRect dst;
+ SkCanvas::Lattice lattice;
+ const sk_sp<Bitmap> bitmap;
+ const sk_sp<SkImage> image;
+
+ SkPaint paint;
+ void draw(SkCanvas* canvas) const {
+ canvas->drawImageLattice(image.get(), lattice, dst, &paint);
+ }
+ ASSERT_DRAWABLE()
+};
+
+template<>
+struct CanvasOp<CanvasOpType::DrawPicture> {
+ sk_sp<SkPicture> picture;
+ void draw(SkCanvas* canvas) const {
+ picture->playback(canvas);
+ }
+};
+
// cleanup our macros
#undef ASSERT_DRAWABLE
diff --git a/libs/hwui/canvas/OpBuffer.h b/libs/hwui/canvas/OpBuffer.h
index 398e090b8cfa..98e385f37a6e 100644
--- a/libs/hwui/canvas/OpBuffer.h
+++ b/libs/hwui/canvas/OpBuffer.h
@@ -156,7 +156,7 @@ private:
using F_PTR = decltype(&f);
using THUNK = void (*)(F_PTR, void*);
static constexpr auto jump = std::array<THUNK, sizeof...(I)>{[](F_PTR fp, void* t) {
- (*fp)(reinterpret_cast<ItemContainer<static_cast<ItemTypes>(I)>*>(t));
+ (*fp)(reinterpret_cast<const ItemContainer<static_cast<ItemTypes>(I)>*>(t));
}...};
// Do the actual iteration of each item
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 772b7a28ef04..f94bae2746d9 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -178,10 +178,8 @@ public:
virtual void restoreToCount(int saveCount) = 0;
virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) = 0;
- virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
- SaveFlags::Flags flags) = 0;
- virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
- SaveFlags::Flags flags) = 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;
virtual int saveUnclippedLayer(int, int, int, int) = 0;
// Matrix
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index dc066da36cbe..b88ffa6e79b8 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -217,6 +217,20 @@ static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
return result;
}
+
+static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring fieldName,
+ jobject typeface) {
+ ScopedUtfChars fieldNameChars(env, fieldName);
+ jfieldID fid =
+ env->GetStaticFieldID(cls, fieldNameChars.c_str(), "Landroid/graphics/Typeface;");
+ if (fid == 0) {
+ jniThrowRuntimeException(env, "Unable to find field");
+ return;
+ }
+ env->SetStaticObjectField(cls, fid, typeface);
+}
+
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gTypefaceMethods[] = {
@@ -237,6 +251,8 @@ static const JNINativeMethod gTypefaceMethods[] = {
(void*)Typeface_registerGenericFamily },
{ "nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
{ "nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
+ { "nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
+ (void*)Typeface_forceSetStaticFinalField },
};
int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index c04340c36511..89fb8bb2a2a0 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -93,16 +93,14 @@ static jint save(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint flagsHandle)
}
static jint saveLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
- jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
+ jfloat r, jfloat b, jlong paintHandle) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
- return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
+ return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint));
}
static jint saveLayerAlpha(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
- jfloat r, jfloat b, jint alpha, jint flagsHandle) {
- SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
- return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
+ jfloat r, jfloat b, jint alpha) {
+ return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha));
}
static jint saveUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint l, jint t, jint r, jint b) {
@@ -688,8 +686,8 @@ static const JNINativeMethod gMethods[] = {
{"nGetWidth","(J)I", (void*) CanvasJNI::getWidth},
{"nGetHeight","(J)I", (void*) CanvasJNI::getHeight},
{"nSave","(JI)I", (void*) CanvasJNI::save},
- {"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
- {"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+ {"nSaveLayer","(JFFFFJ)I", (void*) CanvasJNI::saveLayer},
+ {"nSaveLayerAlpha","(JFFFFI)I", (void*) CanvasJNI::saveLayerAlpha},
{"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer},
{"nRestoreUnclippedLayer","(JIJ)V", (void*) CanvasJNI::restoreUnclippedLayer},
{"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
index a9449b62a1f8..5eaf1853233a 100644
--- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
@@ -32,7 +32,7 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase {
int itemHeight) override {
canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
int length = dp(100);
- canvas.saveLayer(0, 0, length, itemHeight, nullptr, SaveFlags::HasAlphaLayer);
+ canvas.saveLayer(0, 0, length, itemHeight, nullptr);
Paint textPaint;
textPaint.getSkFont().setSize(dp(20));
textPaint.setAntiAlias(true);
diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
index 8630be87c09c..252f539ffca9 100644
--- a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
@@ -47,8 +47,7 @@ public:
// interleave drawText and drawRect with saveLayer ops
for (int i = 0; i < regions; i++, top += smallRectHeight) {
- canvas.saveLayer(bounds.fLeft, top, bounds.fRight, top + padding, &mBluePaint,
- SaveFlags::ClipToLayer | SaveFlags::MatrixClip);
+ canvas.saveLayer(bounds.fLeft, top, bounds.fRight, top + padding, &mBluePaint);
canvas.drawColor(SkColorSetARGB(255, 255, 255, 0), SkBlendMode::kSrcOver);
std::string stri = std::to_string(i);
std::string offscreen = "offscreen line " + stri;
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 97bfba34c790..10ba07905c45 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -33,10 +33,10 @@ public:
card = TestUtils::createNode(0, 0, 400, 800, [](RenderProperties& props, Canvas& canvas) {
// nested clipped saveLayers
- canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer);
+ canvas.saveLayerAlpha(0, 0, 400, 400, 200);
canvas.drawColor(Color::Green_700, SkBlendMode::kSrcOver);
canvas.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
- canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
+ canvas.saveLayerAlpha(100, 100, 300, 300, 128);
canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
canvas.restore();
canvas.restore();
@@ -44,12 +44,14 @@ public:
// single unclipped saveLayer
canvas.save(SaveFlags::MatrixClip);
canvas.translate(0, 400);
- canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::Flags(0)); // unclipped
+ int unclippedSaveLayer = canvas.saveUnclippedLayer(100, 100, 300, 300);
Paint paint;
paint.setAntiAlias(true);
paint.setColor(Color::Green_700);
canvas.drawCircle(200, 200, 200, paint);
- canvas.restore();
+ SkPaint alphaPaint;
+ alphaPaint.setAlpha(128);
+ canvas.restoreUnclippedLayer(unclippedSaveLayer, alphaPaint);
canvas.restore();
});
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index 60c8628ea966..b15c3221dd60 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -22,7 +22,9 @@
#include <tests/common/CallCountingCanvas.h>
+#include "SkPictureRecorder.h"
#include "SkColor.h"
+#include "SkLatticeIter.h"
#include "pipeline/skia/AnimatedDrawables.h"
using namespace android;
@@ -141,6 +143,20 @@ TEST(CanvasOp, lifecycleCheckMove) {
EXPECT_EQ(tracker.alive(), 0);
}
+TEST(CanvasOp, verifyConst) {
+ CanvasOpBuffer buffer;
+ buffer.push<Op::DrawColor>({
+ .color = SkColors::kBlack,
+ .mode = SkBlendMode::kSrcOver,
+ });
+ buffer.for_each([](auto op) {
+ static_assert(std::is_const_v<std::remove_reference_t<decltype(*op)>>,
+ "Expected container to be const");
+ static_assert(std::is_const_v<std::remove_reference_t<decltype(op->op())>>,
+ "Expected op to be const");
+ });
+}
+
TEST(CanvasOp, simplePush) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
@@ -184,7 +200,7 @@ TEST(CanvasOp, simplePush) {
TEST(CanvasOp, simpleDrawPaint) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawColor> {
+ buffer.push<Op::DrawColor> ({
.color = SkColor4f{1, 1, 1, 1},
.mode = SkBlendMode::kSrcIn
});
@@ -199,7 +215,7 @@ TEST(CanvasOp, simpleDrawPaint) {
TEST(CanvasOp, simpleDrawPoint) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawPoint> {
+ buffer.push<Op::DrawPoint> ({
.x = 12,
.y = 42,
.paint = SkPaint{}
@@ -215,7 +231,7 @@ TEST(CanvasOp, simpleDrawPoint) {
TEST(CanvasOp, simpleDrawLine) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawLine> {
+ buffer.push<Op::DrawLine> ({
.startX = 16,
.startY = 28,
.endX = 12,
@@ -233,7 +249,7 @@ TEST(CanvasOp, simpleDrawLine) {
TEST(CanvasOp, simpleDrawRect) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawRect> {
+ buffer.push<Op::DrawRect> ({
.paint = SkPaint{},
.rect = SkRect::MakeEmpty()
});
@@ -250,7 +266,7 @@ TEST(CanvasOp, simpleDrawRegionRect) {
EXPECT_EQ(buffer.size(), 0);
SkRegion region;
region.setRect(SkIRect::MakeWH(12, 50));
- buffer.push(CanvasOp<Op::DrawRegion> {
+ buffer.push<Op::DrawRegion> ({
.paint = SkPaint{},
.region = region
});
@@ -272,7 +288,7 @@ TEST(CanvasOp, simpleDrawRegionPath) {
clip.setRect(SkIRect::MakeWH(100, 100));
SkRegion region;
region.setPath(path, clip);
- buffer.push(CanvasOp<Op::DrawRegion> {
+ buffer.push<Op::DrawRegion> ({
.paint = SkPaint{},
.region = region
});
@@ -287,7 +303,7 @@ TEST(CanvasOp, simpleDrawRegionPath) {
TEST(CanvasOp, simpleDrawRoundRect) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawRoundRect> {
+ buffer.push<Op::DrawRoundRect> ({
.paint = SkPaint{},
.rect = SkRect::MakeEmpty(),
.rx = 10,
@@ -326,7 +342,7 @@ TEST(CanvasOp, simpleDrawDoubleRoundRect) {
innerPts[3].set(10, 10);
innerRRect.setRectRadii(inner, innerPts.get());
- buffer.push(CanvasOp<Op::DrawDoubleRoundRect> {
+ buffer.push<Op::DrawDoubleRoundRect> ({
.outer = outerRRect,
.inner = innerRRect,
.paint = SkPaint{}
@@ -342,7 +358,7 @@ TEST(CanvasOp, simpleDrawDoubleRoundRect) {
TEST(CanvasOp, simpleDrawCircle) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawCircle> {
+ buffer.push<Op::DrawCircle>({
.cx = 5,
.cy = 7,
.radius = 10,
@@ -359,7 +375,7 @@ TEST(CanvasOp, simpleDrawCircle) {
TEST(CanvasOp, simpleDrawOval) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawOval> {
+ buffer.push<Op::DrawOval> ({
.oval = SkRect::MakeEmpty(),
.paint = SkPaint{}
});
@@ -374,7 +390,7 @@ TEST(CanvasOp, simpleDrawOval) {
TEST(CanvasOp, simpleDrawArc) {
CanvasOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0);
- buffer.push(CanvasOp<Op::DrawArc> {
+ buffer.push<Op::DrawArc>({
.oval = SkRect::MakeWH(100, 100),
.startAngle = 120,
.sweepAngle = 70,
@@ -394,7 +410,7 @@ TEST(CanvasOp, simpleDrawPath) {
EXPECT_EQ(buffer.size(), 0);
SkPath path;
path.addCircle(50, 50, 30);
- buffer.push(CanvasOp<Op::DrawPath> {
+ buffer.push<Op::DrawPath> ({
.path = path,
.paint = SkPaint{}
});
@@ -419,7 +435,7 @@ TEST(CanvasOp, simpleDrawRoundRectProperty) {
auto propertyPaint =
sp<uirenderer::CanvasPropertyPaint>(new uirenderer::CanvasPropertyPaint(SkPaint{}));
- buffer.push(CanvasOp<Op::DrawRoundRectProperty> {
+ buffer.push<Op::DrawRoundRectProperty> ({
.left = left,
.top = top,
.right = right,
@@ -446,7 +462,7 @@ TEST(CanvasOp, simpleDrawCircleProperty) {
auto propertyPaint =
sp<uirenderer::CanvasPropertyPaint>(new uirenderer::CanvasPropertyPaint(SkPaint{}));
- buffer.push(CanvasOp<Op::DrawCircleProperty> {
+ buffer.push<Op::DrawCircleProperty> ({
.x = x,
.y = y,
.radius = radius,
@@ -468,7 +484,7 @@ TEST(CanvasOp, simpleDrawVertices) {
SkColor colors[3] = {SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN};
sk_sp<SkVertices> vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, 3, pts,
nullptr, colors);
- buffer.push(CanvasOp<Op::DrawVertices> {
+ buffer.push<Op::DrawVertices> ({
.vertices = vertices,
.mode = SkBlendMode::kSrcOver,
.paint = SkPaint{}
@@ -481,6 +497,110 @@ TEST(CanvasOp, simpleDrawVertices) {
EXPECT_EQ(1, canvas.sumTotalDrawCalls());
}
+TEST(CanvasOp, simpleDrawImage) {
+ CanvasOpBuffer buffer;
+ EXPECT_EQ(buffer.size(), 0);
+
+ SkImageInfo info =SkImageInfo::Make(5, 1,
+ kGray_8_SkColorType, kOpaque_SkAlphaType);
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
+ buffer.push<Op::DrawImage> ({
+ bitmap,
+ 7,
+ 19,
+ SkPaint{}
+ }
+ );
+
+ CallCountingCanvas canvas;
+ EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+ rasterizeCanvasBuffer(buffer, &canvas);
+ EXPECT_EQ(1, canvas.drawImageCount);
+ EXPECT_EQ(1, canvas.sumTotalDrawCalls());
+}
+
+TEST(CanvasOp, simpleDrawImageRect) {
+ CanvasOpBuffer buffer;
+ EXPECT_EQ(buffer.size(), 0);
+
+ SkImageInfo info = SkImageInfo::Make(5, 1,
+ kGray_8_SkColorType, kOpaque_SkAlphaType);
+
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
+ buffer.push<Op::DrawImageRect> ({
+ bitmap, SkRect::MakeWH(100, 100),
+ SkRect::MakeLTRB(120, 110, 220, 210),
+ SkPaint{}
+ }
+ );
+
+ CallCountingCanvas canvas;
+ EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+ rasterizeCanvasBuffer(buffer, &canvas);
+ EXPECT_EQ(1, canvas.drawImageRectCount);
+ EXPECT_EQ(1, canvas.sumTotalDrawCalls());
+}
+
+TEST(CanvasOp, simpleDrawImageLattice) {
+ CanvasOpBuffer buffer;
+ EXPECT_EQ(buffer.size(), 0);
+
+ SkBitmap skBitmap;
+ skBitmap.allocPixels(SkImageInfo::MakeN32Premul(60, 60));
+
+ const int xDivs[] = { 20, 50 };
+ const int yDivs[] = { 10, 40 };
+ SkCanvas::Lattice::RectType fillTypes[3][3];
+ memset(fillTypes, 0, sizeof(fillTypes));
+ fillTypes[1][1] = SkCanvas::Lattice::kTransparent;
+ SkColor colors[9];
+ SkCanvas::Lattice lattice = { xDivs, yDivs, fillTypes[0], 2,
+ 2, nullptr, colors };
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(&skBitmap);
+ buffer.push<Op::DrawImageLattice>(
+ {
+ bitmap,
+ SkRect::MakeWH(5, 1),
+ lattice,
+ SkPaint{}
+ }
+ );
+
+ CallCountingCanvas canvas;
+ EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+ rasterizeCanvasBuffer(buffer, &canvas);
+ EXPECT_EQ(1, canvas.drawImageLatticeCount);
+ EXPECT_EQ(1, canvas.sumTotalDrawCalls());
+}
+
+TEST(CanvasOp, simpleDrawPicture) {
+ CanvasOpBuffer buffer;
+ EXPECT_EQ(buffer.size(), 0);
+
+ SkPictureRecorder recorder;
+ SkCanvas* pictureCanvas = recorder.beginRecording({64, 64, 192, 192});
+ SkPaint paint;
+ pictureCanvas->drawRect(SkRect::MakeWH(200, 200), paint);
+ paint.setColor(SK_ColorWHITE);
+ pictureCanvas->drawRect(SkRect::MakeLTRB(20, 20, 180, 180), paint);
+ sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
+ buffer.push<Op::DrawPicture> ({
+ .picture = picture
+ });
+
+ CallCountingCanvas canvas;
+ EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+ rasterizeCanvasBuffer(buffer, &canvas);
+ // Note because we are explicitly issuing 2 drawRect calls
+ // in the picture recorder above, when it is played back into
+ // CallCountingCanvas we will see 2 calls to drawRect instead of 1
+ // call to drawPicture.
+ // This is because SkiaCanvas::drawPicture uses picture.playback(canvas)
+ // instead of canvas->drawPicture.
+ EXPECT_EQ(2, canvas.drawRectCount);
+ EXPECT_EQ(2, canvas.sumTotalDrawCalls());
+}
+
TEST(CanvasOp, immediateRendering) {
auto canvas = std::make_shared<CallCountingCanvas>();
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 7aa6be8722cf..abdf9d587189 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -198,7 +198,7 @@ TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) {
EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
// note we don't pass SaveFlags::MatrixClip, but matrix and clip will be saved
- recorder.saveLayer(0, 0, 400, 400, &layerPaint, SaveFlags::ClipToLayer);
+ recorder.saveLayer(0, 0, 400, 400, &layerPaint);
ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 400), getRecorderClipBounds(recorder));
EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());