summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Shell/Android.bp8
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig10
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml28
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml6
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml7
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml13
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/styles.xml1
-rw-r--r--libs/WindowManager/Shell/shared/res/values/dimen.xml4
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IHomeTransitionListener.aidl6
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ContextUtils.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ContextUtils.kt)2
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt6
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskUnfoldTransitionMerger.kt33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt78
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java76
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionController.kt115
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt222
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt105
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt113
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java79
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt109
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ButtonBackgroundDrawableUtils.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt88
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt2
-rw-r--r--libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt30
-rw-r--r--libs/WindowManager/Shell/tests/flicker/Android.bp16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt40
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt26
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt463
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt60
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionControllerTest.kt165
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt98
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt367
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt198
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java19
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java71
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java26
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java144
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt49
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt5
-rw-r--r--libs/androidfw/AssetManager2.cpp20
-rw-r--r--libs/androidfw/LocaleDataLookup.cpp2
-rw-r--r--libs/androidfw/ResourceTypes.cpp16
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h11
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h5
-rw-r--r--libs/androidfw/tests/AssetManager2_test.cpp15
-rw-r--r--libs/androidfw/tests/data/flagged/AndroidManifest.xml20
-rw-r--r--libs/androidfw/tests/data/flagged/R.h35
-rwxr-xr-xlibs/androidfw/tests/data/flagged/build28
-rw-r--r--libs/androidfw/tests/data/flagged/flagged.apkbin0 -> 1837 bytes
-rw-r--r--libs/androidfw/tests/data/flagged/res/xml/flagged.xml18
-rw-r--r--libs/hwui/Android.bp8
-rw-r--r--libs/hwui/hwui/Bitmap.cpp9
-rw-r--r--libs/hwui/jni/Bitmap.cpp18
-rw-r--r--libs/hwui/jni/GIFMovie.cpp10
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp13
-rw-r--r--libs/hwui/renderthread/VulkanManager.h8
211 files changed, 3423 insertions, 1480 deletions
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index bcb6c4f555f7..033c934056d6 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -26,9 +26,11 @@ package {
java_library {
name: "wm_shell_protolog-groups",
srcs: [
- ":protolog-common-src",
"src/com/android/wm/shell/protolog/ShellProtoLogGroup.java",
],
+ static_libs: [
+ "protolog-common-lib",
+ ],
}
filegroup {
@@ -159,12 +161,12 @@ java_library {
android_library {
name: "WindowManager-Shell",
srcs: [
- ":wm_shell_protolog_src",
// TODO(b/168581922) protologtool do not support kotlin(*.kt)
- "src/com/android/wm/shell/EventLogTags.logtags",
":wm_shell-aidls",
":wm_shell-shared-aidls",
":wm_shell-sources-kt",
+ ":wm_shell_protolog_src",
+ "src/com/android/wm/shell/EventLogTags.logtags",
],
resource_dirs: [
"res",
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 19455a313a9d..e873dc7c94e8 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -179,6 +179,16 @@ flag {
}
flag {
+ name: "fix_missing_user_change_callbacks"
+ namespace: "multitasking"
+ description: "Fix a race condition that could make Shell miss a user change callback."
+ bug: "404251029"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_bubble_bar_on_phones"
namespace: "multitasking"
description: "Try out bubble bar on phones"
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml
new file mode 100644
index 000000000000..d407884d3fcf
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#1C1C14"
+ android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
+ <path
+ android:fillColor="#1C1C14"
+ android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
index dd1a1b1dca13..75ec2ab9f6f9 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
@@ -18,15 +18,15 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:id="@+id/indicator_solid">
<shape android:shape="rectangle">
- <solid android:color="@androidprv:color/materialColorPrimaryContainer" />
+ <solid android:color="@androidprv:color/materialColorPrimaryFixed" />
<corners android:radius="28dp" />
</shape>
</item>
<item android:id="@+id/indicator_stroke">
<shape android:shape="rectangle">
<corners android:radius="28dp" />
- <stroke android:width="1dp"
- android:color="@androidprv:color/materialColorPrimaryContainer"/>
+ <stroke android:width="2dp"
+ android:color="@androidprv:color/materialColorPrimaryFixed"/>
</shape>
</item>
</layer-list>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index c33669636be4..30acf1ac6eda 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -163,6 +163,13 @@
android:text="@string/change_aspect_ratio_text"
android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
style="@style/DesktopModeHandleMenuActionButton"/>
+
+ <com.android.wm.shell.windowdecor.HandleMenuActionButton
+ android:id="@+id/handle_menu_restart_button"
+ android:contentDescription="@string/handle_menu_restart_text"
+ android:text="@string/handle_menu_restart_text"
+ android:src="@drawable/desktop_mode_ic_handle_menu_restart"
+ style="@style/DesktopModeHandleMenuActionButton"/>
</LinearLayout>
<LinearLayout
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 05c4c56a5c81..f42fea6a0d17 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nuwe venster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Bestuur vensters"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Verander aspekverhouding"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Maak toe"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (werkskermvensters)"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 450419dcc40d..c65bb3822ec2 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"አዲስ መስኮት"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"መስኮቶችን አስተዳድር"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ምጥጥነ ገፅታ ለውጥ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ዴስክቶፕ መስኮት)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 70a23b73b6f5..d06d99203245 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"يمكن العثور على قائمة التطبيقات هنا"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"يمكنك الدخول إلى وضع عرض المحتوى في النافذة الحالية على سطح المكتب لفتح عدة تطبيقات معًا"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"يمكنك الرجوع إلى وضع ملء الشاشة في أي وقت من قائمة التطبيقات"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"استخدام تطبيقات متعدّدة في وقت واحد"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"اسحب تطبيقًا آخر لاستخدام وضع تقسيم الشاشة."</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"مقبض التطبيق"</string>
<string name="app_icon_text" msgid="2823268023931811747">"رمز التطبيق"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ملء الشاشة"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"عرض المحتوى في النافذة الحالية على سطح المكتب"</string>
<string name="split_screen_text" msgid="1396336058129570886">"تقسيم الشاشة"</string>
<string name="more_button_text" msgid="3655388105592893530">"المزيد"</string>
<string name="float_button_text" msgid="9221657008391364581">"نافذة عائمة"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"نافذة جديدة"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"إدارة النوافذ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغيير نسبة العرض إلى الارتفاع"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"إغلاق"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> (عرض المحتوى في النافذة الحالية على سطح المكتب)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"تغيير الحجم"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"لا يمكن نقل التطبيق إلى هنا"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index b1826db57a2c..1d1a048675ac 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"নতুন ৱিণ্ড’"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ৱিণ্ড’ পৰিচালনা কৰক"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"আকাৰৰ অনুপাত সলনি কৰক"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ ৱিণ্ড’ৱিং)"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index c5493b573d0f..2770ede1a85a 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Yeni pəncərə"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pəncərələri idarə edin"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tərəflər nisbətini dəyişin"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Bağlayın"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Masaüstü pəncərə rejimi)"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 307c47ab48eb..615b558a23a9 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljajte prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promeni razmeru"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prozorski prikaz za računare)"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index c53e37c67cfc..d83ed575a34f 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Новае акно"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Кіраваць вокнамі"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змяніць суадносіны бакоў"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Закрыць"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (рэжым вокнаў працоўнага стала)"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 29af2ed1c38b..9b91d3d540e9 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управление на прозорците"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промяна на съотношението"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Затваряне"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим за настолни компютри)"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index d4488a220ac7..9fd156fe6dcd 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"অ্যাপ মেনু এখানে খুঁজে পাওয়া যাবে"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"একসাথে একাধিক অ্যাপ খোলার জন্য ডেস্কটপ উইন্ডোইংয়ে এন্টার করুন"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"অ্যাপ মেনু থেকে ফুল-স্ক্রিন মোডে যেকোনও সময়ে ফিরে আসুন"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"দেখুন ও আরও অনেক কিছু করুন"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"স্প্লিট স্ক্রিনের ক্ষেত্রে অন্য কোনও অ্যাপ টেনে আনুন"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"অ্যাপের হ্যান্ডেল"</string>
<string name="app_icon_text" msgid="2823268023931811747">"অ্যাপ আইকন"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ফুলস্ক্রিন"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ডেস্কটপ উইন্ডোইং"</string>
<string name="split_screen_text" msgid="1396336058129570886">"স্প্লিট স্ক্রিন"</string>
<string name="more_button_text" msgid="3655388105592893530">"আরও"</string>
<string name="float_button_text" msgid="9221657008391364581">"ফ্লোট"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"নতুন উইন্ডো"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"উইন্ডো ম্যানেজ করুন"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ উইন্ডোইং)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ছোট বড় করুন"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"অ্যাপটি এখানে সরানো যাবে না"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 537afdcc6de4..4834ad86db67 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -100,7 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ovdje možete pronaći meni aplikacije"</string>
- <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Otvorite prikaz u prozorima na računalu da biste otvorili više aplikacija zajedno"</string>
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Ulazak u računarski prikaz prozora radi istovremenog otvaranja više aplikacija"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Povratak na prikaz preko cijelog ekrana bilo kada putem menija aplikacije"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Pogledajte i učinite više"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite još jednu aplikaciju za podijeljeni ekran"</string>
@@ -121,7 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Ručica aplikacije"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Cijeli ekran"</string>
- <string name="desktop_text" msgid="9058641752519570266">"Prikaz u prozorima na računalu"</string>
+ <string name="desktop_text" msgid="9058641752519570266">"Računarski prikaz prozora"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Podijeljeni ekran"</string>
<string name="more_button_text" msgid="3655388105592893530">"Više"</string>
<string name="float_button_text" msgid="9221657008391364581">"Lebdeći"</string>
@@ -132,9 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promjena formata slike"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string>
- <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz u prozorima na računalu)"</string>
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (računarski prikaz prozora)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Promijeni veličinu"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ne možete premjestiti aplikaciju ovdje"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 42b07ef3d049..662bd81fe460 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Finestra nova"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestiona les finestres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Canvia la relació d\'aspecte"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Tanca"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (enfinestrament d\'escriptori)"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 44548682cbbc..12c9e294e9c4 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Spravovat okna"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Změnit poměr stran"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zavřít"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (okna na ploše)"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 4d14f93d7b77..5df06ea2b9f7 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nyt vindue"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduer"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Skift billedformat"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Luk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (vinduer på skrivebordet)"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 82bbfc4eff29..b3444e0ac2b8 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Neues Fenster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Fenster verwalten"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Seitenverhältnis ändern"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Schließen"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop-Freiform-Fenster)"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index a8696aff1f0c..c137513b4d7c 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Νέο παράθυρο"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Διαχείριση παραθύρων"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Αλλαγή λόγου διαστάσεων"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Προσαρμογή σε παράθυρο στην επιφάνεια εργασίας)"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 61b68a2b2515..a3156bc551eb 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -132,9 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"New window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
- <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktop windowing)"</string>
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 0802f83ab837..3e2de7cd3b56 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -132,6 +132,7 @@
<string name="new_window_text" msgid="6318648868380652280">"New Window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage Windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
+ <string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimize View"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 61b68a2b2515..a3156bc551eb 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -132,9 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"New window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
- <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktop windowing)"</string>
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 61b68a2b2515..a3156bc551eb 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -132,9 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"New window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
- <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktop windowing)"</string>
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index bb70f6e419a5..6027f7df4272 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"El menú de la app se encuentra aquí"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Entra a la renderización en ventanas de escritorio para abrir varias apps a la vez"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Regresa a pantalla completa en cualquier momento desde el menú de la app"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Aprovecha más"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra otra app para el modo de pantalla dividida"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Controlador de la app"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ícono de la app"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Renderización en ventanas de escritorio"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
<string name="more_button_text" msgid="3655388105592893530">"Más"</string>
<string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nueva ventana"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Administrar ventanas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (renderización en ventanas de escritorio)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Dividir pantalla"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"No se puede mover la app aquí"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index a6595aa9292d..81c9e1b214c4 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"El menú de la aplicación se encuentra aquí"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Entra en el escritorio basado en ventanas si quieres abrir varias aplicaciones a la vez"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vuelve a la pantalla completa en cualquier momento desde el menú de aplicaciones"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta más información y haz más"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra otra aplicación para activar la pantalla dividida"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Controlador de la aplicación"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Icono de la aplicación"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Escritorio basado en ventanas"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
<string name="more_button_text" msgid="3655388105592893530">"Más"</string>
<string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Ventana nueva"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestionar ventanas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (escritorio basado en ventanas)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Dividir pantalla"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"La aplicación no se puede mover aquí"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index c0e4eb36b541..f43348d1f2dc 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Uus aken"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Akende haldamine"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Kuvasuhte muutmine"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Sule"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (töölaua aknad)"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 446839a8eb06..4ac27c2c248e 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Aplikazioaren menua dago hemen"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Sartu ordenagailuan leihoak erabiltzeko modua aplikazio bat baino gehiago batera irekitzeko"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Pantaila osoko modura itzultzeko, erabili aplikazioaren menua"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ikusi eta egin gauza gehiago"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Pantaila zatitua ikusteko, arrastatu beste aplikazio bat"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Aplikazioaren kontrol-puntua"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Aplikazioaren ikonoa"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pantaila osoa"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Ordenagailuan leihoak erabiltzeko modua"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Pantaila zatitzea"</string>
<string name="more_button_text" msgid="3655388105592893530">"Gehiago"</string>
<string name="float_button_text" msgid="9221657008391364581">"Leiho gainerakorra"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Leiho berria"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Kudeatu leihoak"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Aldatu aspektu-erlazioa"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Itxi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ordenagailuan leihoak erabiltzeko modua)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Aldatu tamaina"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikazioa ezin da hona ekarri"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 4879965d1ae7..93a9438ba045 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"پنجره جدید"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"مدیریت کردن پنجره‌ها"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغییر نسبت ابعادی"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"بستن"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (پردازش پنجره‌ای رایانه)"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 8fd6b1bacbbe..7c9e6e6c5229 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Uusi ikkuna"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Hallinnoi ikkunoita"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Vaihda kuvasuhdetta"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Sulje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (työpöydän ikkunointi)"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index b729ececfccd..c78b3130cdac 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier les proportions"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fenêtrage du bureau)"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index ed87a1388304..708212fe77bc 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier le format"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fenêtrage de bureau)"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index a2b871120464..a0a4df17edfe 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Ventá nova"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Xestionar as ventás"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar a proporción"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Pechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (escritorio baseado en ventás)"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index ddef9e1fd07b..2a546fa2a23c 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"નવી વિન્ડો"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"વિન્ડો મેનેજ કરો"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"સાપેક્ષ ગુણોત્તર બદલો"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ડેસ્કટૉપ વિન્ડોઇંગ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index bbe43a1727f8..c2eb2b207740 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"नई विंडो"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"विंडो मैनेज करें"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"बंद करें"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप विंडोविंग)"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 80ee56102cf2..41315fe466c2 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promijeni omjer slike"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz u prozorima na računalu)"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index d24e4da8c982..0ebf2cb23946 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -100,7 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Az alkalmazásmenü itt található"</string>
- <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Asztali ablakkezelési módba lépve több alkalmazást nyithat meg egyidejűleg"</string>
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Asztali ablakkezelési módba lépve egyidejűleg több alkalmazást is megnyithat"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Az alkalmazásmenüből bármikor visszatérhet a teljes képernyőre"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Több mindent láthat és tehet"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Húzzon ide egy másik alkalmazást az osztott képernyő használatához"</string>
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Új ablak"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Ablakok kezelése"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Méretarány módosítása"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Bezárás"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Asztali ablakkezelési mód)"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 6bc3c37cadf4..a792f5bd8844 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Նոր պատուհան"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Կառավարել պատուհանները"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Փոխել կողմերի հարաբերակցությունը"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Փակել"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (համակարգչային պատուհաններ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index c15c2ea6600b..720104511ce6 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Jendela Baru"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Kelola Jendela"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ubah rasio aspek"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Mode jendela desktop)"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 7d98d3b01fc2..8488c308e725 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nýr gluggi"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Stjórna gluggum"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Breyta myndhlutfalli"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Loka"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (gluggastilling í tölvu)"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 72f805693146..4fc60ddc51f8 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nuova finestra"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestisci finestre"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambia proporzioni"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Chiudi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (windowing del desktop)"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index c3e85230c319..be14b0081451 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר ללחוץ כדי לחזור לגרסה הקודמת"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר ללחוץ כדי לסגור."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"תפריט האפליקציה נמצא כאן"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"‏כדי לפתוח כמה אפליקציות יחד, צריך להיכנס למצב \"שינוי דינמי של חלונות במחשב\" (desktop windowing)"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"אפשר לחזור למסך מלא בכל שלב מתפריט האפליקציה"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"רוצה לראות ולעשות יותר?"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"צריך לגרור אפליקציה אחרת כדי להשתמש במסך המפוצל"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"נקודת אחיזה לאפליקציה"</string>
<string name="app_icon_text" msgid="2823268023931811747">"סמל האפליקציה"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"מסך מלא"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"שינוי דינמי של חלונות במחשב"</string>
<string name="split_screen_text" msgid="1396336058129570886">"מסך מפוצל"</string>
<string name="more_button_text" msgid="3655388105592893530">"עוד"</string>
<string name="float_button_text" msgid="9221657008391364581">"בלונים"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"חלון חדש"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ניהול החלונות"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"שינוי יחס הגובה-רוחב"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"סגירה"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> (שינוי דינמי של חלונות במחשב)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"שינוי הגודל"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"לא ניתן להעביר את האפליקציה לכאן"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index c95ec4ee25a3..761df5cc560c 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"新しいウィンドウ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ウィンドウを管理する"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"アスペクト比を変更"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"閉じる"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(デスクトップ ウィンドウ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 0c7264cf9ede..d5c44fb8a963 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"ახალი ფანჯარა"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ფანჯრების მართვა"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"თანაფარდობის შეცვლა"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"დახურვა"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (დესკტოპის ფანჯრის რეჟიმი)"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index c1085db12f08..3a9711e2337a 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -100,7 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Қолданба мәзірін осы жерден табуға болады."</string>
- <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Бірнеше қолданбаны бірге ашу үшін жұмыс үстелі көрінісіне кіріңіз."</string>
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Бірнеше қолданбаны бірге ашу үшін компьютерлік терезелер режиміне кіріңіз."</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Қолданба мәзірінен кез келген уақытта толық экранға оралыңыз."</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Қосымша ақпаратты қарап, әрекеттер жасау"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлу үшін басқа қолданбаға өтіңіз."</string>
@@ -121,7 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Қолданба идентификаторы"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Қолданба белгішесі"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Толық экран"</string>
- <string name="desktop_text" msgid="9058641752519570266">"Жұмыс үстелі көрінісі"</string>
+ <string name="desktop_text" msgid="9058641752519570266">"Компьютерлік терезелер режимі"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Экранды бөлу"</string>
<string name="more_button_text" msgid="3655388105592893530">"Қосымша"</string>
<string name="float_button_text" msgid="9221657008391364581">"Қалқыма"</string>
@@ -132,9 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Жаңа терезе"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Терезелерді басқару"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Арақатынасты өзгерту"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Жабу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string>
- <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (жұмыс үстелі көрінісі)"</string>
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (компьютерлік терезелер режимі)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Өлшемін өзгерту"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Қолданба бұл жерге қойылмайды."</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index afbd9e0c9422..c4b5a9b2009f 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"វិនដូ​ថ្មី"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"គ្រប់គ្រង​វិនដូ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ប្ដូរ​​សមាមាត្រ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"បិទ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"បិទ​ម៉ឺនុយ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (មុខងារវិនដូកុំព្យូទ័រ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 34076e8ecebe..9d3ec2a347af 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"ಹೊಸ ವಿಂಡೋ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ವಿಂಡೋಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ಡೆಸ್ಕ್‌ಟಾಪ್ ವಿಂಡೋಯಿಂಗ್)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index a0fa7e480663..5206b83ef17a 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"새 창"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"창 관리"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"가로세로 비율 변경"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"닫기"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(데스크톱 윈도윙)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 629070cbe810..810a63841cfb 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Жаңы терезе"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Терезелерди тескөө"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Тараптардын катнашын өзгөртүү"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Жабуу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Иш тактанын терезелери)"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index f2d0e6bd7af4..7a4fb61bdf0d 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"ໜ້າຈໍໃໝ່"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ຈັດການໜ້າຈໍ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ປ່ຽນອັດຕາສ່ວນຮູບ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ປິດ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ໜ້າຈໍເດັສທັອບ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index ed4b14cd94dd..75619eaefd17 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Naujas langas"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Tvarkyti langus"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Keisti kraštinių santykį"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Uždaryti"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ (versijos staliniams kompiuteriams rodinys)"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index c24b43a686c3..5eea17c60053 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Jauns logs"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pārvaldīt logus"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mainīt malu attiecību"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Aizvērt"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (darbvirsmas logošana)"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 06ed3232514b..4653aa2ca7c5 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управувајте со прозорците"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени го соодносот"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Затворете"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим со прозорци на работната површина)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index dd4ec1bbf1cd..50c2f6e1a8b2 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"പുതിയ വിന്‍ഡോ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"വിൻഡോകൾ മാനേജ് ചെയ്യുക"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"വീക്ഷണ അനുപാതം മാറ്റുക"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ഡെസ്ക്ടോപ്പ് വിൻഡോയിംഗ്)"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 8683827ae004..80e83a5a3901 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Шинэ цонх"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Цонхнуудыг удирдах"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Аспектын харьцааг өөрчлөх"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Хаах"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Дэлгэцийн цонх үүсгэх онцлог)"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index b7b3fb673d96..deb0bafa2058 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"नवीन विंडो"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"विंडो व्यवस्थापित करा"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"आस्पेक्ट रेशो बदला"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"बंद करा"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप विंडोइंग)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 0896b5ee88cb..1f56033e8c97 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Tetingkap Baharu"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Urus Tetingkap"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tukar nisbah bidang"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Tetingkap desktop)"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 0f336e828f47..061ad0405d89 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"ဝင်းဒိုးအသစ်"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ဝင်းဒိုးများ စီမံရန်"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"အချိုးအစား ပြောင်းရန်"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ဒက်စ်တော့ဝင်းဒိုးမုဒ်)"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 3207614c013f..232fb2b04f92 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nytt vindu"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduene"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Endre høyde/bredde-forholdet"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Lukk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (datamaskin-vindusvisning)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 2c7be991dd67..2d6ab7d816ab 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"नयाँ विन्डो"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"विन्डोहरू व्यवस्थापन गर्नुहोस्"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटप विन्डोइङ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 099f875e0eb9..23f251f8ef60 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nieuw venster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Vensters beheren"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Beeldverhouding wijzigen"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Sluiten"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktopvensterfunctie)"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 19cc8ced6517..8b7f0c758ac7 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ଆପ ମେନୁ ଏଠାରେ ମିଳିପାରିବ"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ଏକାଠି ଏକାଧିକ ଆପ୍ସ ଖୋଲିବାକୁ ଡେସ୍କଟପ ୱିଣ୍ଡୋଇଂରେ ଏଣ୍ଟର କରନ୍ତୁ"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ଆପ ମେନୁରୁ ଯେ କୌଣସି ସମୟରେ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ଫେରନ୍ତୁ"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ଦେଖନ୍ତୁ ଏବଂ ଆହୁରି ଅନେକ କିଛି କରନ୍ତୁ"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ପାଇଁ ଅନ୍ୟ ଏକ ଆପକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"ଆପର ହେଣ୍ଡେଲ"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ଆପ ଆଇକନ"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ପୂର୍ଣ୍ଣସ୍କ୍ରିନ"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ଡେସ୍କଟପ ୱିଣ୍ଡୋଇଂ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ"</string>
<string name="more_button_text" msgid="3655388105592893530">"ଅଧିକ"</string>
<string name="float_button_text" msgid="9221657008391364581">"ଫ୍ଲୋଟ"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"ନୂଆ ୱିଣ୍ଡୋ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ୱିଣ୍ଡୋଗୁଡ଼ିକୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ଡେସ୍କଟପ ୱିଣ୍ଡୋଇଂ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ରିସାଇଜ କରନ୍ତୁ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ଆପକୁ ଏଠାକୁ ମୁଭ କରାଯାଇପାରିବ ନାହିଁ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 50e3cd6a3022..e074a073de00 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"ਨਵੀਂ ਵਿੰਡੋ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ਵਿੰਡੋਆਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ਡੈਸਕਟਾਪ ਵਿੰਡੋ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 58a691f83cca..861d94723317 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Tu znajdziesz menu aplikacji"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Aby otworzyć kilka aplikacji jednocześnie, przejdź do trybu okien na pulpicie"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Z menu aplikacji w każdej chwili możesz wrócić do pełnego ekranu"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobacz i zrób więcej"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Aby podzielić ekran, przeciągnij drugą aplikację"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Uchwyt aplikacji"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacji"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pełny ekran"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Tryb okien na pulpicie"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Podzielony ekran"</string>
<string name="more_button_text" msgid="3655388105592893530">"Więcej"</string>
<string name="float_button_text" msgid="9221657008391364581">"Pływające"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nowe okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Zarządzaj oknami"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmień format obrazu"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zamknij"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zamknij menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (tryb okien na pulpicie)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Zmień rozmiar"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Nie można przenieść aplikacji tutaj"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 2d9bc2bc5b80..53db421ac0f0 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu do app está aqui"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Abra vários apps ao mesmo tempo usando o modo janela para computador"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volte para a tela cheia a qualquer momento no menu do app"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outro app para dividir a tela"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Identificador do app"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ícone do app"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Modo janela para computador"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
<string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modo janela para computador)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionar"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index d5535183551e..5a4da3c07389 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gerir janelas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Alterar formato"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (janelas de computador)"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 2d9bc2bc5b80..53db421ac0f0 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu do app está aqui"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Abra vários apps ao mesmo tempo usando o modo janela para computador"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volte para a tela cheia a qualquer momento no menu do app"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outro app para dividir a tela"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Identificador do app"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ícone do app"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Modo janela para computador"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
<string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modo janela para computador)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionar"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 286443c69d72..159040082a40 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Fereastră nouă"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestionează ferestrele"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Schimbă raportul de dimensiuni"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Închide"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ferestre pe desktop)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 472a239ca344..bd7fa6e72b35 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Новое окно"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управление окнами"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Изменить соотношение сторон"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Закрыть"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыть меню"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим компьютерных окон)"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 6c91955a425e..7a72f99034ac 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"නව කවුළුව"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"කවුළු කළමනාකරණය කරන්න"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"දර්ශන අනුපාතය වෙනස් කරන්න"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"වසන්න"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ඩෙස්ක්ටොප් කවුළුකරණය)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 08404d016789..f0f3e50dccd4 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Spravovať okná"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmeniť pomer strán"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zavrieť"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (windowing na pracovnej ploche)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index deed0e0fe27a..d86cf6baca67 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Novo okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje oken"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Sprememba razmerja stranic"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zapri"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (namizni način prikaza več oken hkrati)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index c6b8c6da56c2..ca4fe6fd3615 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Dritare e re"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Menaxho dritaret"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ndrysho raportin e pamjes"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Mbyll"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ndërfaqja me dritare në desktop)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 3fe25f9d3d9e..58d9398d8d6c 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Нови прозор"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управљајте прозорима"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени размеру"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Затворите"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (прозорски приказ за рачунаре)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 404bdaf6294d..5231a673d1f6 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Nytt fönster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Hantera fönster"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ändra bildformat"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Stäng"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fönsterstapling)"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 3bd7988874b8..9c3c10734a00 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Dirisha Jipya"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Dhibiti Windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Badilisha uwiano"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Funga"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Kupanga madirisha ya kompyuta ya mezani)"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 12780dfd1747..811fce3b2cb1 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"புதிய சாளரம்"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"சாளரங்களை நிர்வகிக்கலாம்"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"தோற்ற விகிதத்தை மாற்று"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"மூடும்"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (டெஸ்க்டாப் சாளரமாக்குதல்)"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 2044fe70c7c3..7a809f4d4684 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"కొత్త విండో"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"విండోలను మేనేజ్ చేయండి"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ఆకార నిష్పత్తిని మార్చండి"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"మూసివేయండి"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"మెనూను మూసివేయండి"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (డెస్క్‌టాప్ వీక్షణ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 60632ada32bb..66996356587e 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ดูเมนูแอปที่นี่ได้"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"เข้าสู่หน้าต่างเดสก์ท็อปเพื่อเปิดหลายแอปพร้อมกัน"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"กลับไปที่โหมดเต็มหน้าจอได้ทุกเมื่อจากเมนูแอป"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"รับชมและทำสิ่งต่างๆ ได้มากขึ้น"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ลากไปไว้ในแอปอื่นเพื่อแยกหน้าจอ"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"แฮนเดิลแอป"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ไอคอนแอป"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"เต็มหน้าจอ"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"หน้าต่างเดสก์ท็อป"</string>
<string name="split_screen_text" msgid="1396336058129570886">"แยกหน้าจอ"</string>
<string name="more_button_text" msgid="3655388105592893530">"เพิ่มเติม"</string>
<string name="float_button_text" msgid="9221657008391364581">"ล่องลอย"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"หน้าต่างใหม่"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"จัดการหน้าต่าง"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"เปลี่ยนสัดส่วนการแสดงผล"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ปิด"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ปิดเมนู"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (หน้าต่างเดสก์ท็อป)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ปรับขนาด"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ย้ายแอปมาที่นี่ไม่ได้"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 586d655d7901..82c085bcdbe7 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Bagong Window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pamahalaan ang Mga Window"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Baguhin ang aspect ratio"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Isara"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Isara ang Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 9605acfb9a8b..4d6775a9102d 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Yeni Pencere"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pencereleri yönet"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"En boy oranını değiştir"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Kapat"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (masaüstü pencereleme)"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 54af06762029..4b2aad06cec6 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Тут ви знайдете меню додатка"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Щоб відкрити кілька додатків одночасно, перейдіть у режим вікон робочого стола"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"З меню додатка можна будь-коли повернутися в повноекранний режим"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Більше простору та можливостей"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Щоб перейти в режим розділення екрана, перетягніть сюди інший додаток"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Дескриптор додатка"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Значок додатка"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"На весь екран"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Режим вікон робочого стола"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Розділити екран"</string>
<string name="more_button_text" msgid="3655388105592893530">"Більше"</string>
<string name="float_button_text" msgid="9221657008391364581">"Плаваюче вікно"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Нове вікно"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Керувати вікнами"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змінити формат"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Закрити"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрити меню"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим вікон робочого стола)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Розгорнути екран"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Змінити розмір"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Сюди не можна перемістити додаток"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 9614ce9112f7..f9972d229062 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"نئی ونڈو"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ونڈوز کا نظم کریں"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تناسبی شرح کو تبدیل کریں"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"بند کریں"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"مینیو بند کریں"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ڈیسک ٹاپ ونڈوئنگ)"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 6025467ea44b..231cb738940b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Yangi oyna"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Oynalarni boshqarish"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tomonlar nisbatini oʻzgartirish"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Yopish"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop rejimidagi oynalar)"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 4c394b2aec46..88ef07186dab 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -121,7 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Ô điều khiển ứng dụng"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Biểu tượng ứng dụng"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Toàn màn hình"</string>
- <string name="desktop_text" msgid="9058641752519570266">"Cửa sổ trên máy tính"</string>
+ <string name="desktop_text" msgid="9058641752519570266">"Chế độ cửa sổ trên máy tính"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Chia đôi màn hình"</string>
<string name="more_button_text" msgid="3655388105592893530">"Tuỳ chọn khác"</string>
<string name="float_button_text" msgid="9221657008391364581">"Nổi"</string>
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Cửa sổ mới"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Quản lý cửa sổ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Thay đổi tỷ lệ khung hình"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Đóng"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Chế độ cửa sổ trên máy tính)"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index b29711df14b9..a07767d0d09a 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"您可以在此处找到应用菜单"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"进入桌面设备窗口化模式可同时打开多个应用"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"随时从应用菜单返回全屏模式"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"查看和处理更多任务"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一个应用,即可使用分屏模式"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"应用手柄"</string>
<string name="app_icon_text" msgid="2823268023931811747">"应用图标"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"全屏"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"桌面设备窗口化"</string>
<string name="split_screen_text" msgid="1396336058129570886">"分屏"</string>
<string name="more_button_text" msgid="3655388105592893530">"更多"</string>
<string name="float_button_text" msgid="9221657008391364581">"悬浮"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"新窗口"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"管理窗口"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"更改宽高比"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"关闭"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"关闭菜单"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(桌面设备窗口化)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"最大化屏幕"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"调整大小"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"无法将应用移至此处"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index fda5c744eccf..103ee600ff60 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -100,7 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可在這裡找到應用程式選單"</string>
- <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"進入電腦分割視窗模式可同時開啟多個應用程式"</string>
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"進入桌面電腦視窗模式以同時開啟多個應用程式"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你可隨時從應用程式選單返回全螢幕"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一個應用程式即可分割螢幕"</string>
@@ -121,7 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"應用程式控點"</string>
<string name="app_icon_text" msgid="2823268023931811747">"應用程式圖示"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"全螢幕"</string>
- <string name="desktop_text" msgid="9058641752519570266">"電腦分割視窗"</string>
+ <string name="desktop_text" msgid="9058641752519570266">"桌面電腦視窗模式"</string>
<string name="split_screen_text" msgid="1396336058129570886">"分割螢幕"</string>
<string name="more_button_text" msgid="3655388105592893530">"更多"</string>
<string name="float_button_text" msgid="9221657008391364581">"浮動"</string>
@@ -132,9 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"新視窗"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更長寬比"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"關閉"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
- <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (電腦分割視窗)"</string>
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (桌面電腦視窗模式)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"調整大小"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至這裡"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index e83c647b59bb..a3d81fc6f4f0 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -100,7 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可以在這裡查看應用程式選單"</string>
- <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"進入電腦分割視窗模式可同時開啟多個應用程式"</string>
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"進入電腦分割視窗模式後,可同時開啟多個應用程式"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你隨時可以從應用程式選單返回全螢幕模式"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖進另一個應用程式即可使用分割畫面模式"</string>
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"新視窗"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更顯示比例"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"關閉"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (電腦分割視窗)"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 4d658f291e02..81d9200d6938 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -132,6 +132,8 @@
<string name="new_window_text" msgid="6318648868380652280">"Iwindi Elisha"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Phatha Amawindi"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Shintsha ukubukeka kwesilinganiselo"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Vala"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Vala Imenyu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Ukwenziwa kwamawindi amaningi kwedeskithophu)"</string>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 8d18f959951b..5732fc936b47 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -68,8 +68,6 @@
<color name="desktop_mode_caption_button_on_hover_light">#11000000</color>
<color name="desktop_mode_caption_button_on_hover_dark">#11FFFFFF</color>
<color name="desktop_mode_caption_button">#00000000</color>
- <color name="tiling_divider_background_light">#C9C7B6</color>
- <color name="tiling_divider_background_dark">#4A4739</color>
<color name="tiling_handle_background_light">#000000</color>
<color name="tiling_handle_background_dark">#FFFFFF</color>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index e68680219349..ca18c97f9127 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -304,6 +304,8 @@
<dimen name="bubble_transform_area_width">140dp</dimen>
<!-- Width of the box at the corner of the screen where drag leads to app moving to bubble -->
<dimen name="bubble_transform_area_height">140dp</dimen>
+ <!-- How much elevation a bubble ui needs when dragged, must be above drop target & dismiss. -->
+ <dimen name="dragged_bubble_elevation">3dp</dimen>
<!-- Bottom and end margin for compat buttons. -->
<dimen name="compat_button_margin">24dp</dimen>
@@ -534,10 +536,10 @@
pill elevation. -->
<dimen name="desktop_mode_handle_menu_width">218dp</dimen>
- <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each plus
- additional actions pill 208dp plus 2dp spacing between them plus 4dp top padding
- plus 2dp bottom padding: 52*3 + 52*4 + (4-1)*2 + 4 + 2 = 376 -->
- <dimen name="desktop_mode_handle_menu_height">376dp</dimen>
+ <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each,
+ additional actions pill 260dp, plus 2dp spacing between them plus 4dp top padding.
+ 52*3 + 52*5 + (5-1)*2 + 4 = 428 -->
+ <dimen name="desktop_mode_handle_menu_height">428dp</dimen>
<!-- The elevation set on the handle menu pills. -->
<dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
@@ -566,6 +568,9 @@
<!-- The height of the handle menu's "Change aspect ratio" pill in desktop mode. -->
<dimen name="desktop_mode_handle_menu_change_aspect_ratio_height">52dp</dimen>
+ <!-- The height of the handle menu's "Optimize View" pill in desktop mode. -->
+ <dimen name="desktop_mode_handle_menu_restart_button_height">52dp</dimen>
+
<!-- The margin between pills of the handle menu in desktop mode. -->
<dimen name="desktop_mode_handle_menu_pill_spacing_margin">2dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 5ef83826840b..1fd4704f7814 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -314,6 +314,8 @@
<string name="manage_windows_text">Manage Windows</string>
<!-- Accessibility text for the handle menu change aspect ratio button [CHAR LIMIT=NONE] -->
<string name="change_aspect_ratio_text">Change aspect ratio</string>
+ <!-- Accessibility text for the handle menu restart button [CHAR LIMIT=NONE] -->
+ <string name="handle_menu_restart_text">Optimize View</string>
<!-- Accessibility text for the handle menu close button [CHAR LIMIT=NONE] -->
<string name="close_text">Close</string>
<!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 08cda7b94a78..086c8a5651c3 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -51,7 +51,6 @@
<item name="android:clickable">true</item>
<item name="android:focusable">true</item>
<item name="android:orientation">horizontal</item>
- <item name="android:background">?android:attr/selectableItemBackground</item>
</style>
<style name="DesktopModeHandleMenuActionButtonImage">
diff --git a/libs/WindowManager/Shell/shared/res/values/dimen.xml b/libs/WindowManager/Shell/shared/res/values/dimen.xml
index 3b504cf713f1..74b6023bde36 100644
--- a/libs/WindowManager/Shell/shared/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/shared/res/values/dimen.xml
@@ -39,9 +39,9 @@
<dimen name="drag_zone_v_split_from_expanded_view_height_fold_short">100dp</dimen>
<!-- Bubble drop target dimensions -->
- <dimen name="drop_target_elevation">1dp</dimen>
+ <dimen name="drop_target_elevation">2dp</dimen>
<dimen name="drop_target_radius">28dp</dimen>
- <dimen name="drop_target_stroke">1dp</dimen>
+ <dimen name="drop_target_stroke">2dp</dimen>
<dimen name="drop_target_full_screen_padding">20dp</dimen>
<dimen name="drop_target_desktop_window_padding_small">100dp</dimen>
<dimen name="drop_target_desktop_window_padding_large">130dp</dimen>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IHomeTransitionListener.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IHomeTransitionListener.aidl
index 8481c446c6aa..8dcda53b602b 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IHomeTransitionListener.aidl
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IHomeTransitionListener.aidl
@@ -18,6 +18,7 @@ package com.android.wm.shell.shared;
import android.window.RemoteTransition;
import android.window.TransitionFilter;
+import android.view.InsetsState;
/**
* Listener interface that Launcher attaches to SystemUI to get home activity transition callbacks
@@ -29,5 +30,10 @@ oneway interface IHomeTransitionListener {
* Called when a transition changes the visibility of the home activity on the default display.
*/
void onHomeVisibilityChanged(in boolean isVisible);
+
+ /**
+ * Called when the insets at display-level change.
+ */
+ void onDisplayInsetsChanged(in InsetsState insets);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ContextUtils.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ContextUtils.kt
index 0b36f452348a..27db5297b758 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ContextUtils.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ContextUtils.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.bubbles
+package com.android.wm.shell.shared.bubbles
import android.content.Context
import android.view.View
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
index 73277310ffe4..df101fe44b75 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
@@ -30,15 +30,15 @@ import com.android.wm.shell.shared.R
class DropTargetView(context: Context) : View(context) {
private val rectPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
+ color = context.getColor(com.android.internal.R.color.materialColorPrimaryFixed)
style = Paint.Style.FILL
alpha = (0.35f * 255).toInt()
}
private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
+ color = context.getColor(com.android.internal.R.color.materialColorPrimaryFixed)
style = Paint.Style.STROKE
- strokeWidth = 1.dpToPx()
+ strokeWidth = 2.dpToPx()
}
private val cornerRadius = 28.dpToPx()
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index e5a4cd034e72..a9224b02ad31 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -451,6 +451,6 @@ public class DesktopModeStatus {
pw.println(maxTaskLimitHandle == null ? "null" : maxTaskLimitHandle.getInt(/* def= */ -1));
pw.print(innerPrefix); pw.print("showAppHandle config override=");
- pw.print(overridesShowAppHandle(context));
+ pw.println(overridesShowAppHandle(context));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
index 8e78686ac13d..711667760314 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
@@ -34,6 +34,8 @@ import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import android.view.animation.TranslateAnimation;
+import com.android.wm.shell.shared.animation.Interpolators;
+
import java.util.function.Consumer;
/**
@@ -66,7 +68,7 @@ public class SizeChangeAnimation {
* The maximum of stretching applied to any surface during interpolation (since the animation
* is a combination of stretching/cropping/fading).
*/
- private static final float SCALE_FACTOR = 0.7f;
+ private static final float DEFAULT_SCALE_FACTOR = 0.7f;
/**
* Since this animation is made of several sub-animations, we want to pre-arrange the
@@ -82,13 +84,27 @@ public class SizeChangeAnimation {
*/
private static final int ANIMATION_RESOLUTION = 1000;
+ /**
+ * Initialize a size-change animation from start to end bounds
+ */
public SizeChangeAnimation(Rect startBounds, Rect endBounds) {
- this(startBounds, endBounds, 1f);
+ this(startBounds, endBounds, 1f, DEFAULT_SCALE_FACTOR);
}
- public SizeChangeAnimation(Rect startBounds, Rect endBounds, float initialScale) {
- mAnimation = buildContainerAnimation(startBounds, endBounds, initialScale);
- mSnapshotAnim = buildSnapshotAnimation(startBounds, endBounds);
+ /**
+ * Initialize a size-change animation from start to end bounds.
+ * <p>
+ * Allows specifying the initial scale factor, {@code initialScale}, that is applied to the
+ * start bounds. This can be useful for example when a task is scaled down when the size change
+ * animation starts.
+ * <p>
+ * By default the max scale applied to any surface is {@link #DEFAULT_SCALE_FACTOR}. Use
+ * {@code scaleFactor} to override it.
+ */
+ public SizeChangeAnimation(Rect startBounds, Rect endBounds, float initialScale,
+ float scaleFactor) {
+ mAnimation = buildContainerAnimation(startBounds, endBounds, initialScale, scaleFactor);
+ mSnapshotAnim = buildSnapshotAnimation(startBounds, endBounds, scaleFactor);
}
/**
@@ -172,16 +188,18 @@ public class SizeChangeAnimation {
/** Animation for the whole container (snapshot is inside this container). */
private static AnimationSet buildContainerAnimation(Rect startBounds, Rect endBounds,
- float initialScale) {
+ float initialScale, float scaleFactor) {
final long duration = ANIMATION_RESOLUTION;
boolean growing = endBounds.width() - startBounds.width()
+ endBounds.height() - startBounds.height() >= 0;
- long scalePeriod = (long) (duration * SCALE_FACTOR);
- float startScaleX = SCALE_FACTOR * ((float) startBounds.width()) / endBounds.width()
- + (1.f - SCALE_FACTOR);
- float startScaleY = SCALE_FACTOR * ((float) startBounds.height()) / endBounds.height()
- + (1.f - SCALE_FACTOR);
+ long scalePeriod = (long) (duration * scaleFactor);
+ float startScaleX = scaleFactor * ((float) startBounds.width()) / endBounds.width()
+ + (1.f - scaleFactor);
+ float startScaleY = scaleFactor * ((float) startBounds.height()) / endBounds.height()
+ + (1.f - scaleFactor);
final AnimationSet animSet = new AnimationSet(true);
+ // Use a linear interpolator so the driving ValueAnimator sets the interpolation
+ animSet.setInterpolator(Interpolators.LINEAR);
final Animation scaleAnim = new ScaleAnimation(startScaleX, 1, startScaleY, 1);
scaleAnim.setDuration(scalePeriod);
@@ -218,17 +236,20 @@ public class SizeChangeAnimation {
}
/** The snapshot surface is assumed to be a child of the container surface. */
- private static AnimationSet buildSnapshotAnimation(Rect startBounds, Rect endBounds) {
+ private static AnimationSet buildSnapshotAnimation(Rect startBounds, Rect endBounds,
+ float scaleFactor) {
final long duration = ANIMATION_RESOLUTION;
boolean growing = endBounds.width() - startBounds.width()
+ endBounds.height() - startBounds.height() >= 0;
- long scalePeriod = (long) (duration * SCALE_FACTOR);
- float endScaleX = 1.f / (SCALE_FACTOR * ((float) startBounds.width()) / endBounds.width()
- + (1.f - SCALE_FACTOR));
- float endScaleY = 1.f / (SCALE_FACTOR * ((float) startBounds.height()) / endBounds.height()
- + (1.f - SCALE_FACTOR));
+ long scalePeriod = (long) (duration * scaleFactor);
+ float endScaleX = 1.f / (scaleFactor * ((float) startBounds.width()) / endBounds.width()
+ + (1.f - scaleFactor));
+ float endScaleY = 1.f / (scaleFactor * ((float) startBounds.height()) / endBounds.height()
+ + (1.f - scaleFactor));
AnimationSet snapAnimSet = new AnimationSet(true);
+ // Use a linear interpolator so the driving ValueAnimator sets the interpolation
+ snapAnimSet.setInterpolator(Interpolators.LINEAR);
// Animation for the "old-state" snapshot that is atop the task.
final Animation snapAlphaAnim = new AlphaAnimation(1.f, 0.f);
snapAlphaAnim.setDuration(scalePeriod);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
index 26f7b360e0fc..98cae5ae9296 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
@@ -19,6 +19,8 @@ package com.android.wm.shell.back;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+import static com.android.window.flags.Flags.enableMultidisplayTrackpadBackGesture;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Color;
@@ -59,9 +61,9 @@ public class BackAnimationBackground {
* @param statusbarHeight The height of the statusbar (in px).
*/
public void ensureBackground(Rect startRect, int color,
- @NonNull SurfaceControl.Transaction transaction, int statusbarHeight) {
+ @NonNull SurfaceControl.Transaction transaction, int statusbarHeight, int displayId) {
ensureBackground(startRect, color, transaction, statusbarHeight,
- null /* cropBounds */, 0 /* cornerRadius */);
+ null /* cropBounds */, 0 /* cornerRadius */, displayId);
}
/**
@@ -76,7 +78,7 @@ public class BackAnimationBackground {
*/
public void ensureBackground(Rect startRect, int color,
@NonNull SurfaceControl.Transaction transaction, int statusbarHeight,
- @Nullable Rect cropBounds, float cornerRadius) {
+ @Nullable Rect cropBounds, float cornerRadius, int displayId) {
if (mBackgroundSurface != null) {
return;
}
@@ -91,7 +93,11 @@ public class BackAnimationBackground {
.setCallsite("BackAnimationBackground")
.setColorLayer();
- mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder);
+ if (enableMultidisplayTrackpadBackGesture()) {
+ mRootTaskDisplayAreaOrganizer.attachToDisplayArea(displayId, colorLayerBuilder);
+ } else {
+ mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder);
+ }
mBackgroundSurface = colorLayerBuilder.build();
transaction.setColor(mBackgroundSurface, colorComponents)
.setLayer(mBackgroundSurface, BACKGROUND_LAYER)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index e29e98562cfa..f91154c7a362 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -286,6 +286,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
this::createExternalInterface, this);
mShellCommandHandler.addDumpCallback(this::dump, this);
mShellController.addConfigurationChangeListener(this);
+ registerBackGestureDelegate();
}
public BackAnimation getBackAnimationImpl() {
@@ -508,7 +509,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
mShouldStartOnNextMoveEvent = false;
} else {
- mShouldStartOnNextMoveEvent = true;
+ if (predictiveBackDelayWmTransition()) {
+ onGestureStarted(touchX, touchY, swipeEdge);
+ } else {
+ mShouldStartOnNextMoveEvent = true;
+ }
}
}
} else if (keyAction == MotionEvent.ACTION_MOVE) {
@@ -1141,6 +1146,32 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mBackAnimationAdapter = new BackAnimationAdapter(runner);
}
+ private void registerBackGestureDelegate() {
+ if (!Flags.delegateBackGestureToShell()) {
+ return;
+ }
+ final RemoteCallback requestBackMonitor = new RemoteCallback(
+ new RemoteCallback.OnResultListener() {
+ @Override
+ public void onResult(@Nullable Bundle result) {
+ mShellExecutor.execute(() -> {
+ if (mBackGestureStarted) {
+ Log.w(TAG, "Back gesture is running, ignore request");
+ return;
+ }
+ onMotionEvent(0, 0, KeyEvent.ACTION_DOWN, EDGE_NONE);
+ setTriggerBack(true);
+ onMotionEvent(0, 0, KeyEvent.ACTION_UP, EDGE_NONE);
+ });
+ }
+ });
+ try {
+ mActivityTaskManager.registerBackGestureDelegate(requestBackMonitor);
+ } catch (RemoteException remoteException) {
+ Log.w(TAG, "Failed register back gesture request ", remoteException);
+ }
+ }
+
/**
* Description of current BackAnimationController state.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index 7e5a82e640cc..6c41c975cf4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -52,6 +52,7 @@ import com.android.internal.jank.Cuj
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.internal.policy.SystemBarUtils
import com.android.internal.protolog.ProtoLog
+import com.android.window.flags.Flags.enableMultidisplayTrackpadBackGesture
import com.android.window.flags.Flags.predictiveBackTimestampApi
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -210,7 +211,8 @@ abstract class CrossActivityBackAnimation(
statusbarHeight,
if (closingTarget!!.windowConfiguration.tasksAreFloating())
closingTarget!!.localBounds else null,
- cornerRadius
+ cornerRadius,
+ closingTarget!!.taskInfo.getDisplayId()
)
ensureScrimLayer()
if (isLetterboxed && enteringHasSameLetterbox) {
@@ -409,7 +411,12 @@ abstract class CrossActivityBackAnimation(
.setOpaque(false)
.setHidden(false)
- rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, scrimBuilder)
+ if (enableMultidisplayTrackpadBackGesture()) {
+ rootTaskDisplayAreaOrganizer.attachToDisplayArea(
+ closingTarget!!.taskInfo.getDisplayId(), scrimBuilder)
+ } else {
+ rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, scrimBuilder)
+ }
scrimLayer = scrimBuilder.build()
val colorComponents = floatArrayOf(0f, 0f, 0f)
maxScrimAlpha = if (isDarkTheme) MAX_SCRIM_ALPHA_DARK else MAX_SCRIM_ALPHA_LIGHT
@@ -473,7 +480,13 @@ abstract class CrossActivityBackAnimation(
.setOpaque(true)
.setHidden(false)
- rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, letterboxBuilder)
+ if (enableMultidisplayTrackpadBackGesture()) {
+ rootTaskDisplayAreaOrganizer.attachToDisplayArea(
+ closingTarget!!.taskInfo.getDisplayId(), letterboxBuilder)
+ } else {
+ rootTaskDisplayAreaOrganizer.attachToDisplayArea(
+ Display.DEFAULT_DISPLAY, letterboxBuilder)
+ }
val layer = letterboxBuilder.build()
val colorComponents =
floatArrayOf(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index f48b3ffcd598..f5b0e359e019 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -175,7 +175,8 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
// Draw background.
mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(),
- BACKGROUNDCOLOR, mTransaction, mStatusbarHeight);
+ BACKGROUNDCOLOR, mTransaction, mStatusbarHeight,
+ mClosingTarget.taskInfo.getDisplayId());
mInterWindowMargin = mContext.getResources()
.getDimension(R.dimen.cross_task_back_inter_window_margin);
mVerticalMargin = mContext.getResources()
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 81eff6f7399a..6a7b5cc0e1ba 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
@@ -81,6 +81,7 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.ScreenCapture;
import android.window.ScreenCapture.SynchronousScreenCaptureListener;
+import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -120,6 +121,7 @@ import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation.UpdateSource;
import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider;
+import com.android.wm.shell.shared.bubbles.ContextUtils;
import com.android.wm.shell.shared.bubbles.DeviceConfig;
import com.android.wm.shell.shared.draganddrop.DragAndDropConstants;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
@@ -155,7 +157,7 @@ import java.util.function.IntConsumer;
*/
public class BubbleController implements ConfigurationChangeListener,
RemoteCallable<BubbleController>, Bubbles.SysuiProxy.Provider,
- BubbleBarDragListener {
+ BubbleBarDragListener, BubbleTaskUnfoldTransitionMerger {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
@@ -2174,6 +2176,32 @@ public class BubbleController implements ConfigurationChangeListener,
});
}
+ @Override
+ public boolean mergeTaskWithUnfold(@NonNull ActivityManager.RunningTaskInfo taskInfo,
+ @NonNull TransitionInfo.Change change,
+ @NonNull SurfaceControl.Transaction startT,
+ @NonNull SurfaceControl.Transaction finishT) {
+ if (!mBubbleTransitions.mTaskViewTransitions.isTaskViewTask(taskInfo)) {
+ // if this task isn't managed by bubble transitions just bail.
+ return false;
+ }
+ if (isShowingAsBubbleBar()) {
+ // if bubble bar is enabled, the task view will switch to a new surface on unfold, so we
+ // should not merge the transition.
+ return false;
+ }
+
+ boolean merged = mBubbleTransitions.mTaskViewTransitions.updateBoundsForUnfold(
+ change.getEndAbsBounds(), startT, finishT, change.getTaskInfo(), change.getLeash());
+ if (merged) {
+ BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
+ if (selectedBubble != null && selectedBubble.getExpandedView() != null) {
+ selectedBubble.getExpandedView().onContainerClipUpdate();
+ }
+ }
+ return merged;
+ }
+
/** When bubbles are floating, this will be used to notify the floating views. */
private final BubbleViewCallback mBubbleStackViewCallback = new BubbleViewCallback() {
@Override
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 290ef1633819..ac8393576477 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
@@ -843,7 +843,8 @@ public class BubbleExpandedView extends LinearLayout {
onContainerClipUpdate();
}
- private void onContainerClipUpdate() {
+ /** Updates the clip bounds. */
+ public void onContainerClipUpdate() {
if (mTopClip == 0 && mBottomClip == 0 && mRightClip == 0 && mLeftClip == 0) {
if (mIsClipping) {
mIsClipping = false;
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 3dce45690cf2..7ae9de8ee65d 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
@@ -94,6 +94,7 @@ import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
+import com.android.wm.shell.shared.bubbles.ContextUtils;
import com.android.wm.shell.shared.bubbles.DeviceConfig;
import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
@@ -2958,6 +2959,9 @@ public class BubbleStackView extends FrameLayout
if (mIsExpanded) {
mExpandedViewAnimationController.animateForImeVisibilityChange(visible);
BubbleExpandedView expandedView = getExpandedView();
+ if (expandedView != null) {
+ expandedView.setImeVisible(visible);
+ }
if (mPositioner.showBubblesVertically() && expandedView != null) {
float selectedY = mPositioner.getExpandedBubbleXY(getState().selectedIndex,
getState()).y;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskUnfoldTransitionMerger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskUnfoldTransitionMerger.kt
new file mode 100644
index 000000000000..13fabc8b1d91
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskUnfoldTransitionMerger.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles
+
+import android.app.ActivityManager
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+
+/** Merges a bubble task transition with the unfold transition. */
+interface BubbleTaskUnfoldTransitionMerger {
+
+ /** Attempts to merge the transition. Returns `true` if the change was merged. */
+ fun mergeTaskWithUnfold(
+ taskInfo: ActivityManager.RunningTaskInfo,
+ change: TransitionInfo.Change,
+ startT: SurfaceControl.Transaction,
+ finishT: SurfaceControl.Transaction
+ ): Boolean
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index fa22a3961002..ea365efcb400 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -596,11 +596,11 @@ public class BubbleBarAnimationHelper {
final Size size = getExpandedViewSize();
Point position = getExpandedViewRestPosition(size);
- final SizeChangeAnimation sca =
- new SizeChangeAnimation(
- new Rect(origBounds.left - position.x, origBounds.top - position.y,
- origBounds.right - position.x, origBounds.bottom - position.y),
- new Rect(0, 0, size.getWidth(), size.getHeight()), origScale);
+ Rect startBounds = new Rect(origBounds.left - position.x, origBounds.top - position.y,
+ origBounds.right - position.x, origBounds.bottom - position.y);
+ Rect endBounds = new Rect(0, 0, size.getWidth(), size.getHeight());
+ final SizeChangeAnimation sca = new SizeChangeAnimation(startBounds, endBounds,
+ origScale, /* scaleFactor= */ 1f);
sca.initialize(bbev, taskLeash, snapshot, startT);
Animator a = sca.buildViewAnimator(bbev, tvSf, snapshot, /* onFinish */ (va) -> {
@@ -615,7 +615,7 @@ public class BubbleBarAnimationHelper {
bbev.setSurfaceZOrderedOnTop(true);
a.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION);
- a.setInterpolator(Interpolators.EMPHASIZED);
+ a.setInterpolator(EMPHASIZED);
a.start();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index 35435569d8b1..44d859dfb9ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -17,9 +17,11 @@
package com.android.wm.shell.bubbles.bar
import android.annotation.SuppressLint
+import android.content.Context
import android.view.MotionEvent
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.DismissView
@@ -32,6 +34,7 @@ import com.android.wm.shell.shared.magnetictarget.MagnetizedObject
/** Controller for handling drag interactions with [BubbleBarExpandedView] */
@SuppressLint("ClickableViewAccessibility")
class BubbleBarExpandedViewDragController(
+ private val context: Context,
private val expandedView: BubbleBarExpandedView,
private val dismissView: DismissView,
private val animationHelper: BubbleBarAnimationHelper,
@@ -54,6 +57,8 @@ class BubbleBarExpandedViewDragController(
MagnetizedObject.magnetizeView(expandedView)
private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget
+ private val draggedBubbleElevation: Float
+
init {
magnetizedExpandedView.magnetListener = MagnetListener()
magnetizedExpandedView.animateStuckToTarget =
@@ -70,6 +75,8 @@ class BubbleBarExpandedViewDragController(
MagnetizedObject.MagneticTarget(dismissView.circle, dismissView.circle.width)
magnetizedExpandedView.addTarget(magnetizedDismissTarget)
+ draggedBubbleElevation = context.resources.getDimension(
+ R.dimen.dragged_bubble_elevation)
val dragMotionEventHandler = HandleDragListener()
expandedView.handleView.setOnTouchListener { view, event ->
@@ -103,6 +110,7 @@ class BubbleBarExpandedViewDragController(
override fun onDown(v: View, ev: MotionEvent): Boolean {
// While animating, don't allow new touch events
if (expandedView.isAnimating) return false
+ expandedView.z = draggedBubbleElevation
if (dropTargetManager != null && dragZoneFactory != null) {
val draggedObject = DraggedObject.ExpandedView(
if (bubblePositioner.isBubbleBarOnLeft) {
@@ -154,11 +162,13 @@ class BubbleBarExpandedViewDragController(
velX: Float,
velY: Float,
) {
+ v.translationZ = 0f
finishDrag()
}
override fun onCancel(v: View, ev: MotionEvent, viewInitialX: Float, viewInitialY: Float) {
isStuckToDismiss = false
+ v.translationZ = 0f
finishDrag()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 2cc9387bd1e9..dd86725f7944 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -349,6 +349,7 @@ public class BubbleBarLayerView extends FrameLayout
}
};
mDragController = new BubbleBarExpandedViewDragController(
+ mContext,
mExpandedView,
mDismissView,
mAnimationHelper,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 97184c859d4d..06d734c71f6a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -287,7 +287,13 @@ public class DisplayController {
? mContext
: mContext.createDisplayContext(display);
final Context context = perDisplayContext.createConfigurationContext(newConfig);
- dr.setDisplayLayout(context, new DisplayLayout(context, display));
+ final DisplayLayout displayLayout = new DisplayLayout(context, display);
+ if (mDisplayTopology != null) {
+ displayLayout.setGlobalBoundsDp(
+ mDisplayTopology.getAbsoluteBounds().get(
+ displayId, displayLayout.globalBoundsDp()));
+ }
+ dr.setDisplayLayout(context, displayLayout);
for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
displayId, newConfig);
@@ -349,6 +355,19 @@ public class DisplayController {
}
}
+ private void onDesktopModeEligibleChanged(int displayId) {
+ synchronized (mDisplays) {
+ if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) {
+ Slog.w(TAG, "Skipping onDesktopModeEligibleChanged on unknown"
+ + " display, displayId=" + displayId);
+ return;
+ }
+ for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) {
+ mDisplayChangedListeners.get(i).onDesktopModeEligibleChanged(displayId);
+ }
+ }
+ }
+
private static class DisplayRecord {
private int mDisplayId;
private Context mContext;
@@ -416,6 +435,13 @@ public class DisplayController {
new ArraySet<>(restricted), new ArraySet<>(unrestricted));
});
}
+
+ @Override
+ public void onDesktopModeEligibleChanged(int displayId) {
+ mMainExecutor.execute(() -> {
+ DisplayController.this.onDesktopModeEligibleChanged(displayId);
+ });
+ }
}
/**
@@ -461,5 +487,10 @@ public class DisplayController {
* Called when the display topology has changed.
*/
default void onTopologyChanged(DisplayTopology topology) {}
+
+ /**
+ * Called when the eligibility of the desktop mode for a display have changed.
+ */
+ default void onDesktopModeEligibleChanged(int displayId) {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
index cf0ecae7c815..a1e7ff04347f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
@@ -106,31 +106,37 @@ public class DividerRoundedCorner extends View {
* of non split screen.
*
* @param isSplitScreen Whether the divider is used by split screen or tiling.
- * @param isDarkMode Whether the mode is ui dark mode.
+ * @param color Rounded corner color.
*/
- public void setup(boolean isSplitScreen, boolean isDarkMode) {
+ public void setup(boolean isSplitScreen, int color) {
mIsSplitScreen = isSplitScreen;
if (!isSplitScreen) {
- mDividerBarBackground.setColor(getTilingHandleColor(isDarkMode));
+ mDividerBarBackground.setColor(color);
}
}
/**
- * Notifies the divider of ui mode change.
+ * Notifies the divider of ui mode change and provides a new color.
*
- * @param isDarkMode Whether the mode is ui dark mode.
+ * @param color The new divider rounded corner color.
*/
- public void onUiModeChange(boolean isDarkMode) {
+ public void onUiModeChange(int color) {
if (!mIsSplitScreen) {
- mDividerBarBackground.setColor(getTilingHandleColor(isDarkMode));
+ mDividerBarBackground.setColor(color);
invalidate();
}
}
- private int getTilingHandleColor(boolean isDarkMode) {
- return isDarkMode ? getResources().getColor(
- R.color.tiling_divider_background_dark, null /* theme */) : getResources().getColor(
- R.color.tiling_divider_background_light, null /* theme */);
+ /**
+ * Notifies rounded corner view of color change.
+ *
+ * @param color The new divider rounded corner color.
+ */
+ public void onCornerColorChange(int color) {
+ if (!mIsSplitScreen) {
+ mDividerBarBackground.setColor(color);
+ invalidate();
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index f62fd819319e..ad509bcc1ceb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -762,9 +762,11 @@ public abstract class WMShellBaseModule {
ShellTaskOrganizer organizer,
TransactionPool pool,
DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellAnimationThread ShellExecutor animExecutor,
+ @ShellAnimationThread Handler animHandler,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
HomeTransitionObserver homeTransitionObserver,
FocusTransitionObserver focusTransitionObserver) {
@@ -773,15 +775,19 @@ public abstract class WMShellBaseModule {
shellInit = new ShellInit(mainExecutor);
}
return new Transitions(context, shellInit, shellCommandHandler, shellController, organizer,
- pool, displayController, mainExecutor, mainHandler, animExecutor,
- rootTaskDisplayAreaOrganizer, homeTransitionObserver, focusTransitionObserver);
+ pool, displayController, displayInsetsController, mainExecutor, mainHandler,
+ animExecutor, animHandler, rootTaskDisplayAreaOrganizer, homeTransitionObserver,
+ focusTransitionObserver);
}
@WMSingleton
@Provides
static HomeTransitionObserver provideHomeTransitionObserver(Context context,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new HomeTransitionObserver(context, mainExecutor);
+ @ShellMainThread ShellExecutor mainExecutor,
+ DisplayInsetsController displayInsetsController,
+ ShellInit shellInit) {
+ return new HomeTransitionObserver(context, mainExecutor, displayInsetsController,
+ shellInit);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 613e78753b66..46c9b07fb802 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -62,6 +62,7 @@ import com.android.wm.shell.bubbles.BubbleEducationController;
import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleResizabilityChecker;
+import com.android.wm.shell.bubbles.BubbleTaskUnfoldTransitionMerger;
import com.android.wm.shell.bubbles.bar.BubbleBarDragListener;
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository;
import com.android.wm.shell.common.DisplayController;
@@ -79,6 +80,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.UserProfileContexts;
import com.android.wm.shell.common.split.SplitState;
+import com.android.wm.shell.compatui.api.CompatUIHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
import com.android.wm.shell.crashhandling.ShellCrashHandler;
@@ -89,6 +91,7 @@ import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopDisplayEventHandler;
import com.android.wm.shell.desktopmode.DesktopDisplayModeController;
+import com.android.wm.shell.desktopmode.DesktopImeHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopMinimizationTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler;
@@ -98,7 +101,6 @@ import com.android.wm.shell.desktopmode.DesktopModeKeyGestureHandler;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopModeMoveToDisplayTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
-import com.android.wm.shell.desktopmode.DesktopPipTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
@@ -242,6 +244,13 @@ public abstract class WMShellModule {
context, logger, positioner, educationController, mainExecutor, bgExecutor);
}
+ @WMSingleton
+ @Provides
+ static Optional<BubbleTaskUnfoldTransitionMerger> provideBubbleTaskUnfoldTransitionMerger(
+ Optional<BubbleController> bubbleController) {
+ return bubbleController.map(controller -> controller);
+ }
+
// Note: Handler needed for LauncherApps.register
@WMSingleton
@Provides
@@ -703,7 +712,8 @@ public abstract class WMShellModule {
Transitions transitions,
@ShellMainThread ShellExecutor executor,
@ShellMainThread Handler handler,
- ShellInit shellInit) {
+ ShellInit shellInit,
+ Optional<BubbleTaskUnfoldTransitionMerger> bubbleTaskUnfoldTransitionMerger) {
return new UnfoldTransitionHandler(
shellInit,
progressProvider.get(),
@@ -712,7 +722,8 @@ public abstract class WMShellModule {
transactionPool,
executor,
handler,
- transitions);
+ transitions,
+ bubbleTaskUnfoldTransitionMerger);
}
@WMSingleton
@@ -781,7 +792,6 @@ public abstract class WMShellModule {
OverviewToDesktopTransitionObserver overviewToDesktopTransitionObserver,
DesksOrganizer desksOrganizer,
Optional<DesksTransitionObserver> desksTransitionObserver,
- Optional<DesktopPipTransitionObserver> desktopPipTransitionObserver,
UserProfileContexts userProfileContexts,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DragToDisplayTransitionHandler dragToDisplayTransitionHandler,
@@ -825,7 +835,6 @@ public abstract class WMShellModule {
overviewToDesktopTransitionObserver,
desksOrganizer,
desksTransitionObserver.get(),
- desktopPipTransitionObserver,
userProfileContexts,
desktopModeCompatPolicy,
dragToDisplayTransitionHandler,
@@ -1044,7 +1053,9 @@ public abstract class WMShellModule {
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
- MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
+ Optional<CompatUIHandler> compatUI,
+ DesksOrganizer desksOrganizer
) {
if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
return Optional.empty();
@@ -1062,7 +1073,8 @@ public abstract class WMShellModule {
activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger,
desktopModeUiEventLogger, taskResourceLoader, recentsTransitionHandler,
desktopModeCompatPolicy, desktopTilingDecorViewModel,
- multiDisplayDragMoveIndicatorController));
+ multiDisplayDragMoveIndicatorController, compatUI.orElse(null),
+ desksOrganizer));
}
@WMSingleton
@@ -1237,7 +1249,6 @@ public abstract class WMShellModule {
Transitions transitions,
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
- Optional<DesktopPipTransitionObserver> desktopPipTransitionObserver,
Optional<BackAnimationController> backAnimationController,
DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider,
ShellInit shellInit) {
@@ -1250,7 +1261,6 @@ public abstract class WMShellModule {
transitions,
shellTaskOrganizer,
desktopMixedTransitionHandler.get(),
- desktopPipTransitionObserver,
backAnimationController.get(),
desktopWallpaperActivityTokenProvider,
shellInit)));
@@ -1272,19 +1282,6 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
- static Optional<DesktopPipTransitionObserver> provideDesktopPipTransitionObserver(
- Context context
- ) {
- if (DesktopModeStatus.canEnterDesktopMode(context)
- && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
- return Optional.of(
- new DesktopPipTransitionObserver());
- }
- return Optional.empty();
- }
-
- @WMSingleton
- @Provides
static Optional<DesktopMixedTransitionHandler> provideDesktopMixedTransitionHandler(
Context context,
Transitions transitions,
@@ -1524,6 +1521,18 @@ public abstract class WMShellModule {
mainHandler));
}
+ @WMSingleton
+ @Provides
+ static Optional<DesktopImeHandler> provideDesktopImeHandler(
+ DisplayImeController displayImeController,
+ Context context,
+ ShellInit shellInit) {
+ if (!DesktopModeStatus.canEnterDesktopMode(context)) {
+ return Optional.empty();
+ }
+ return Optional.of(new DesktopImeHandler(displayImeController, shellInit));
+ }
+
//
// App zoom out
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 6f0919e1d045..c5f956a80702 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.dagger.pip;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
+import android.window.DesktopModeFlags;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -42,6 +43,8 @@ import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.desktopmode.DesktopPipTransitionController;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.pip2.phone.PhonePipMenuController;
@@ -55,6 +58,7 @@ import com.android.wm.shell.pip2.phone.PipTransition;
import com.android.wm.shell.pip2.phone.PipTransitionState;
import com.android.wm.shell.pip2.phone.PipUiStateChangeController;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -91,12 +95,13 @@ public abstract class Pip2Module {
DisplayController displayController,
Optional<SplitScreenController> splitScreenControllerOptional,
PipDesktopState pipDesktopState,
+ Optional<DesktopPipTransitionController> desktopPipTransitionController,
PipInteractionHandler pipInteractionHandler) {
return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
pipBoundsState, null, pipBoundsAlgorithm, pipTaskListener,
pipScheduler, pipStackListenerController, pipDisplayLayoutState,
pipUiStateChangeController, displayController, splitScreenControllerOptional,
- pipDesktopState, pipInteractionHandler);
+ pipDesktopState, desktopPipTransitionController, pipInteractionHandler);
}
@WMSingleton
@@ -250,6 +255,22 @@ public abstract class Pip2Module {
dragToDesktopTransitionHandlerOptional, rootTaskDisplayAreaOrganizer);
}
+ @WMSingleton
+ @Provides
+ static Optional<DesktopPipTransitionController> provideDesktopPipTransitionController(
+ Context context, Optional<DesktopTasksController> desktopTasksControllerOptional,
+ Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
+ PipDesktopState pipDesktopState
+ ) {
+ if (DesktopModeStatus.canEnterDesktopMode(context)
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()) {
+ return Optional.of(
+ new DesktopPipTransitionController(desktopTasksControllerOptional.get(),
+ desktopUserRepositoriesOptional.get(), pipDesktopState));
+ }
+ return Optional.empty();
+ }
+
@BindsOptionalOf
abstract DragToDesktopTransitionHandler optionalDragToDesktopTransitionHandler();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index 3b98f8123b46..80c6f2e5ff33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -49,9 +49,6 @@ class DesktopDisplayEventHandler(
private val desktopDisplayModeController: DesktopDisplayModeController,
) : OnDisplaysChangedListener, OnDeskRemovedListener {
- private val desktopRepository: DesktopRepository
- get() = desktopUserRepositories.current
-
init {
shellInit.addInitCallback({ onInit() }, this)
}
@@ -66,7 +63,7 @@ class DesktopDisplayEventHandler(
object : UserChangeListener {
override fun onUserChanged(newUserId: Int, userContext: Context) {
val displayIds = rootTaskDisplayAreaOrganizer.displayIds
- createDefaultDesksIfNeeded(displayIds.toSet())
+ createDefaultDesksIfNeeded(displayIds.toSet(), newUserId)
}
}
)
@@ -75,38 +72,52 @@ class DesktopDisplayEventHandler(
override fun onDisplayAdded(displayId: Int) {
if (displayId != DEFAULT_DISPLAY) {
- desktopDisplayModeController.refreshDisplayWindowingMode()
+ desktopDisplayModeController.updateExternalDisplayWindowingMode(displayId)
+ // The default display's windowing mode depends on the availability of the external
+ // display. So updating the default display's windowing mode here.
+ desktopDisplayModeController.updateDefaultDisplayWindowingMode()
}
- createDefaultDesksIfNeeded(displayIds = setOf(displayId))
+ createDefaultDesksIfNeeded(displayIds = setOf(displayId), userId = null)
}
override fun onDisplayRemoved(displayId: Int) {
if (displayId != DEFAULT_DISPLAY) {
- desktopDisplayModeController.refreshDisplayWindowingMode()
+ desktopDisplayModeController.updateDefaultDisplayWindowingMode()
}
// TODO: b/362720497 - move desks in closing display to the remaining desk.
}
- override fun onDeskRemoved(lastDisplayId: Int, deskId: Int) {
- val remainingDesks = desktopRepository.getNumberOfDesks(lastDisplayId)
- if (remainingDesks == 0) {
- logV("All desks removed from display#$lastDisplayId")
- createDefaultDesksIfNeeded(setOf(lastDisplayId))
+ override fun onDesktopModeEligibleChanged(displayId: Int) {
+ if (
+ DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue &&
+ displayId != DEFAULT_DISPLAY
+ ) {
+ desktopDisplayModeController.updateExternalDisplayWindowingMode(displayId)
+ // The default display's windowing mode depends on the desktop eligibility of the
+ // external display. So updating the default display's windowing mode here.
+ desktopDisplayModeController.updateDefaultDisplayWindowingMode()
}
}
- private fun createDefaultDesksIfNeeded(displayIds: Set<Int>) {
+ override fun onDeskRemoved(lastDisplayId: Int, deskId: Int) {
+ createDefaultDesksIfNeeded(setOf(lastDisplayId), userId = null)
+ }
+
+ private fun createDefaultDesksIfNeeded(displayIds: Set<Int>, userId: Int?) {
if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
logV("createDefaultDesksIfNeeded displays=%s", displayIds)
mainScope.launch {
desktopRepositoryInitializer.isInitialized.collect { initialized ->
if (!initialized) return@collect
+ val repository =
+ userId?.let { desktopUserRepositories.getProfile(userId) }
+ ?: desktopUserRepositories.current
displayIds
.filter { displayId -> displayId != Display.INVALID_DISPLAY }
.filter { displayId -> supportsDesks(displayId) }
- .filter { displayId -> desktopRepository.getNumberOfDesks(displayId) == 0 }
+ .filter { displayId -> repository.getNumberOfDesks(displayId) == 0 }
.also { displaysNeedingDesk ->
logV(
"createDefaultDesksIfNeeded creating default desks in displays=%s",
@@ -116,7 +127,7 @@ class DesktopDisplayEventHandler(
.forEach { displayId ->
// TODO: b/393978539 - consider activating the desk on creation when
// applicable, such as for connected displays.
- desktopTasksController.createDesk(displayId)
+ desktopTasksController.createDesk(displayId, repository.userId)
}
cancel()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
index ea2fdc0ee8ed..dec489e8fc63 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -56,44 +56,51 @@ class DesktopDisplayModeController(
@ShellMainThread private val mainHandler: Handler,
) {
- private val onTabletModeChangedListener =
- object : InputManager.OnTabletModeChangedListener {
- override fun onTabletModeChanged(whenNanos: Long, inTabletMode: Boolean) {
- refreshDisplayWindowingMode()
- }
- }
-
private val inputDeviceListener =
object : InputManager.InputDeviceListener {
override fun onInputDeviceAdded(deviceId: Int) {
- refreshDisplayWindowingMode()
+ updateDefaultDisplayWindowingMode()
}
override fun onInputDeviceChanged(deviceId: Int) {
- refreshDisplayWindowingMode()
+ updateDefaultDisplayWindowingMode()
}
override fun onInputDeviceRemoved(deviceId: Int) {
- refreshDisplayWindowingMode()
+ updateDefaultDisplayWindowingMode()
}
}
init {
if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
- inputManager.registerOnTabletModeChangedListener(
- onTabletModeChangedListener,
- mainHandler,
- )
inputManager.registerInputDeviceListener(inputDeviceListener, mainHandler)
}
}
- fun refreshDisplayWindowingMode() {
+ fun updateExternalDisplayWindowingMode(displayId: Int) {
+ if (!DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue) return
+
+ val desktopModeSupported =
+ displayController.getDisplay(displayId)?.let { display ->
+ DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+ } ?: false
+ if (!desktopModeSupported) return
+
+ // An external display should always be a freeform display when desktop mode is enabled.
+ updateDisplayWindowingMode(displayId, WINDOWING_MODE_FREEFORM)
+ }
+
+ fun updateDefaultDisplayWindowingMode() {
if (!DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue) return
- val targetDisplayWindowingMode = getTargetWindowingModeForDefaultDisplay()
- val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)
- requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." }
+ updateDisplayWindowingMode(DEFAULT_DISPLAY, getTargetWindowingModeForDefaultDisplay())
+ }
+
+ private fun updateDisplayWindowingMode(displayId: Int, targetDisplayWindowingMode: Int) {
+ val tdaInfo =
+ requireNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)) {
+ "DisplayAreaInfo of display#$displayId must be non-null."
+ }
val currentDisplayWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
if (currentDisplayWindowingMode == targetDisplayWindowingMode) {
// Already in the target mode.
@@ -101,15 +108,16 @@ class DesktopDisplayModeController(
}
logV(
- "As an external display is connected, changing default display's windowing mode from" +
- " ${windowingModeToString(currentDisplayWindowingMode)}" +
- " to ${windowingModeToString(targetDisplayWindowingMode)}"
+ "Changing display#%d's windowing mode from %s to %s",
+ displayId,
+ windowingModeToString(currentDisplayWindowingMode),
+ windowingModeToString(targetDisplayWindowingMode),
)
val wct = WindowContainerTransaction()
wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode)
shellTaskOrganizer
- .getRunningTasks(DEFAULT_DISPLAY)
+ .getRunningTasks(displayId)
.filter { it.activityType == ACTIVITY_TYPE_STANDARD }
.forEach {
// TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy
@@ -125,7 +133,7 @@ class DesktopDisplayModeController(
// The override windowing mode of DesktopWallpaper can be UNDEFINED on fullscreen-display
// right after the first launch while its resolved windowing mode is FULLSCREEN. We here
// it has the FULLSCREEN override windowing mode.
- desktopWallpaperActivityTokenProvider.getToken(DEFAULT_DISPLAY)?.let { token ->
+ desktopWallpaperActivityTokenProvider.getToken(displayId)?.let { token ->
wct.setWindowingMode(token, WINDOWING_MODE_FULLSCREEN)
}
transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
@@ -139,7 +147,7 @@ class DesktopDisplayModeController(
return true
}
if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
- if (isInClamshellMode() || hasAnyMouseDevice()) {
+ if (hasAnyTouchpadDevice() && hasAnyPhysicalKeyboardDevice()) {
return true
}
}
@@ -186,17 +194,25 @@ class DesktopDisplayModeController(
private fun hasExternalDisplay() =
rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY }
- private fun hasAnyMouseDevice() =
- inputManager.inputDeviceIds.any {
- inputManager.getInputDevice(it)?.supportsSource(InputDevice.SOURCE_MOUSE) == true
+ private fun hasAnyTouchpadDevice() =
+ inputManager.inputDeviceIds.any { deviceId ->
+ inputManager.getInputDevice(deviceId)?.let { device ->
+ device.supportsSource(InputDevice.SOURCE_TOUCHPAD) && device.isEnabled()
+ } ?: false
}
- private fun isInClamshellMode() = inputManager.isInTabletMode() == InputManager.SWITCH_STATE_OFF
+ private fun hasAnyPhysicalKeyboardDevice() =
+ inputManager.inputDeviceIds.any { deviceId ->
+ inputManager.getInputDevice(deviceId)?.let { device ->
+ !device.isVirtual() && device.isFullKeyboard() && device.isEnabled()
+ } ?: false
+ }
private fun isDefaultDisplayDesktopEligible(): Boolean {
- val display = requireNotNull(displayController.getDisplay(DEFAULT_DISPLAY)) {
- "Display object of DEFAULT_DISPLAY must be non-null."
- }
+ val display =
+ requireNotNull(displayController.getDisplay(DEFAULT_DISPLAY)) {
+ "Display object of DEFAULT_DISPLAY must be non-null."
+ }
return DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt
new file mode 100644
index 000000000000..93ba71a17937
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.view.SurfaceControl
+import com.android.window.flags.Flags
+import com.android.wm.shell.common.DisplayImeController
+import com.android.wm.shell.common.DisplayImeController.ImePositionProcessor.IME_ANIMATION_DEFAULT
+import com.android.wm.shell.sysui.ShellInit
+
+/** Handles the interactions between IME and desktop tasks */
+class DesktopImeHandler(
+ private val displayImeController: DisplayImeController,
+ shellInit: ShellInit,
+) : DisplayImeController.ImePositionProcessor {
+
+ init {
+ shellInit.addInitCallback(::onInit, this)
+ }
+
+ private fun onInit() {
+ if (Flags.enableDesktopImeBugfix()) {
+ displayImeController.addPositionProcessor(this)
+ }
+ }
+
+ override fun onImeStartPositioning(
+ displayId: Int,
+ hiddenTop: Int,
+ shownTop: Int,
+ showing: Boolean,
+ isFloating: Boolean,
+ t: SurfaceControl.Transaction?,
+ ): Int {
+ return IME_ANIMATION_DEFAULT
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
index b9cb32d8a14f..e0300d688379 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
@@ -167,7 +167,35 @@ class DesktopModeUiEventLogger(
@UiEvent(doc = "Exit desktop mode education tooltip on the app header menu is clicked")
EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_CLICKED(2104),
@UiEvent(doc = "Exit desktop mode education tooltip is dismissed by the user")
- EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_DISMISSED(2105);
+ EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_DISMISSED(2105),
+ @UiEvent(doc = "A11y service opened app handle menu by selecting handle from fullscreen")
+ A11Y_APP_HANDLE_MENU_OPENED(2156),
+ @UiEvent(doc = "A11y service opened app handle menu through Switch Access actions menu ")
+ A11Y_SYSTEM_ACTION_APP_HANDLE_MENU(2157),
+ @UiEvent(doc = "A11y service selected desktop mode from app handle menu")
+ A11Y_APP_HANDLE_MENU_DESKTOP_VIEW(2158),
+ @UiEvent(doc = "A11y service selected fullscreen mode from app handle menu")
+ A11Y_APP_HANDLE_MENU_FULLSCREEN(2159),
+ @UiEvent(doc = "A11y service selected split screen mode from app handle menu")
+ A11Y_APP_HANDLE_MENU_SPLIT_SCREEN(2160),
+ @UiEvent(doc = "A11y service selected maximize/restore button from app header")
+ A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON(2161),
+ @UiEvent(doc = "A11y service selected minimize button from app header")
+ A11Y_APP_WINDOW_MINIMIZE_BUTTON(2162),
+ @UiEvent(doc = "A11y service selected close button from app header")
+ A11Y_APP_WINDOW_CLOSE_BUTTON(2163),
+ @UiEvent(doc = "A11y service selected maximize button from app header maximize menu")
+ A11Y_MAXIMIZE_MENU_MAXIMIZE(2164),
+ @UiEvent(doc = "A11y service selected resize left button from app header maximize menu")
+ A11Y_MAXIMIZE_MENU_RESIZE_LEFT(2165),
+ @UiEvent(doc = "A11y service selected resize right button from app header maximize menu")
+ A11Y_MAXIMIZE_MENU_RESIZE_RIGHT(2166),
+ @UiEvent(doc = "A11y service triggered a11y action to maximize/restore app window")
+ A11Y_ACTION_MAXIMIZE_RESTORE(2167),
+ @UiEvent(doc = "A11y service triggered a11y action to resize app window left")
+ A11Y_ACTION_RESIZE_LEFT(2168),
+ @UiEvent(doc = "A11y service triggered a11y action to resize app window right")
+ A11Y_ACTION_RESIZE_RIGHT(2169);
override fun getId(): Int = mId
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index 3c44fe8061aa..55179511af6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -22,7 +22,6 @@ import android.app.ActivityManager.RunningTaskInfo
import android.app.TaskInfo
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.pm.ActivityInfo.LAUNCH_MULTIPLE
import android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE
import android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK
@@ -303,21 +302,19 @@ fun getInheritedExistingTaskBounds(
// Top task is an instance of launching activity. Activity will be launching in a new
// task with the existing task also being closed. Inherit existing task bounds to
// prevent new task jumping.
- (isLaunchingNewTask(launchMode, intentFlags) && isClosingExitingInstance(intentFlags)) ->
+ (isLaunchingNewSingleTask(launchMode) && isClosingExitingInstance(intentFlags)) ->
lastTask.configuration.windowConfiguration.bounds
else -> null
}
}
/**
- * Returns true if the launch mode or intent will result in a new task being created for the
- * activity.
+ * Returns true if the launch mode will result in a single new task being created for the activity.
*/
-private fun isLaunchingNewTask(launchMode: Int, intentFlags: Int) =
+private fun isLaunchingNewSingleTask(launchMode: Int) =
launchMode == LAUNCH_SINGLE_TASK ||
launchMode == LAUNCH_SINGLE_INSTANCE ||
- launchMode == LAUNCH_SINGLE_INSTANCE_PER_TASK ||
- (intentFlags and FLAG_ACTIVITY_NEW_TASK) != 0
+ launchMode == LAUNCH_SINGLE_INSTANCE_PER_TASK
/**
* Returns true if the intent will result in an existing task instance being closed if a new one
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 1c5138f486e4..8bbe36dd6644 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -57,7 +57,7 @@ import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider;
import com.android.wm.shell.windowdecor.tiling.SnapEventHandler;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -307,7 +307,8 @@ public class DesktopModeVisualIndicator {
if (splitRightRegion.contains(x, y)) {
result = IndicatorType.TO_SPLIT_RIGHT_INDICATOR;
}
- if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+ && mDragStartState == DragStartState.FROM_FULLSCREEN) {
if (calculateBubbleLeftRegion(layout).contains(x, y)) {
result = IndicatorType.TO_BUBBLE_LEFT_INDICATOR;
} else if (calculateBubbleRightRegion(layout).contains(x, y)) {
@@ -415,30 +416,59 @@ public class DesktopModeVisualIndicator {
private List<Pair<Rect, IndicatorType>> initSmallTabletRegions(DisplayLayout layout,
boolean isLeftRightSplit) {
- boolean dragFromFullscreen = mDragStartState == DragStartState.FROM_FULLSCREEN;
- boolean dragFromSplit = mDragStartState == DragStartState.FROM_SPLIT;
- if (isLeftRightSplit && (dragFromFullscreen || dragFromSplit)) {
+ return switch (mDragStartState) {
+ case DragStartState.FROM_FULLSCREEN -> initSmallTabletRegionsFromFullscreen(layout,
+ isLeftRightSplit);
+ case DragStartState.FROM_SPLIT -> initSmallTabletRegionsFromSplit(layout,
+ isLeftRightSplit);
+ default -> Collections.emptyList();
+ };
+ }
+
+ private List<Pair<Rect, IndicatorType>> initSmallTabletRegionsFromFullscreen(
+ DisplayLayout layout, boolean isLeftRightSplit) {
+
+ List<Pair<Rect, IndicatorType>> result = new ArrayList<>();
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ result.add(new Pair<>(calculateBubbleLeftRegion(layout), TO_BUBBLE_LEFT_INDICATOR));
+ result.add(new Pair<>(calculateBubbleRightRegion(layout), TO_BUBBLE_RIGHT_INDICATOR));
+ }
+
+ if (isLeftRightSplit) {
int splitRegionWidth = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.shared.R.dimen.drag_zone_h_split_from_app_width_fold);
- return Arrays.asList(
- new Pair<>(calculateBubbleLeftRegion(layout), TO_BUBBLE_LEFT_INDICATOR),
- new Pair<>(calculateBubbleRightRegion(layout), TO_BUBBLE_RIGHT_INDICATOR),
- new Pair<>(calculateSplitLeftRegion(layout, splitRegionWidth,
- /* captionHeight= */ 0), TO_SPLIT_LEFT_INDICATOR),
- new Pair<>(calculateSplitRightRegion(layout, splitRegionWidth,
- /* captionHeight= */ 0), TO_SPLIT_RIGHT_INDICATOR),
- new Pair<>(new Rect(), TO_FULLSCREEN_INDICATOR) // default to fullscreen
- );
+ result.add(new Pair<>(calculateSplitLeftRegion(layout, splitRegionWidth,
+ /* captionHeight= */ 0), TO_SPLIT_LEFT_INDICATOR));
+ result.add(new Pair<>(calculateSplitRightRegion(layout, splitRegionWidth,
+ /* captionHeight= */ 0), TO_SPLIT_RIGHT_INDICATOR));
}
- if (dragFromFullscreen) {
- // If left/right split is not available, we can only drag fullscreen tasks
- // TODO(b/401352409): add support for top/bottom split zones
- return Arrays.asList(
- new Pair<>(calculateBubbleLeftRegion(layout), TO_BUBBLE_LEFT_INDICATOR),
- new Pair<>(calculateBubbleRightRegion(layout), TO_BUBBLE_RIGHT_INDICATOR),
- new Pair<>(new Rect(), TO_FULLSCREEN_INDICATOR) // default to fullscreen
- );
+ // TODO(b/401352409): add support for top/bottom split zones
+ // default to fullscreen
+ result.add(new Pair<>(new Rect(), TO_FULLSCREEN_INDICATOR));
+ return result;
+ }
+
+ private List<Pair<Rect, IndicatorType>> initSmallTabletRegionsFromSplit(DisplayLayout layout,
+ boolean isLeftRightSplit) {
+ if (!isLeftRightSplit) {
+ // Dragging a top/bottom split is not supported on small tablets
+ return Collections.emptyList();
}
- return Collections.emptyList();
+
+ List<Pair<Rect, IndicatorType>> result = new ArrayList<>();
+ if (BubbleAnythingFlagHelper.enableBubbleAnything()) {
+ result.add(new Pair<>(calculateBubbleLeftRegion(layout), TO_BUBBLE_LEFT_INDICATOR));
+ result.add(new Pair<>(calculateBubbleRightRegion(layout), TO_BUBBLE_RIGHT_INDICATOR));
+ }
+
+ int splitRegionWidth = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.shared.R.dimen.drag_zone_h_split_from_app_width_fold);
+ result.add(new Pair<>(calculateSplitLeftRegion(layout, splitRegionWidth,
+ /* captionHeight= */ 0), TO_SPLIT_LEFT_INDICATOR));
+ result.add(new Pair<>(calculateSplitRightRegion(layout, splitRegionWidth,
+ /* captionHeight= */ 0), TO_SPLIT_RIGHT_INDICATOR));
+ // default to fullscreen
+ result.add(new Pair<>(new Rect(), TO_FULLSCREEN_INDICATOR));
+ return result;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionController.kt
new file mode 100644
index 000000000000..88468531cc47
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionController.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager
+import android.os.IBinder
+import android.window.DesktopExperienceFlags
+import android.window.WindowContainerTransaction
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.common.pip.PipDesktopState
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+
+/**
+ * Controller to perform extra handling to PiP transitions that are entering while in Desktop mode.
+ */
+class DesktopPipTransitionController(
+ private val desktopTasksController: DesktopTasksController,
+ private val desktopUserRepositories: DesktopUserRepositories,
+ private val pipDesktopState: PipDesktopState,
+) {
+
+ /**
+ * This is called by [PipTransition#handleRequest] when a request for entering PiP is received.
+ *
+ * @param wct WindowContainerTransaction that will apply these changes
+ * @param transition that will apply this transaction
+ * @param taskInfo of the task that is entering PiP
+ */
+ fun handlePipTransition(
+ wct: WindowContainerTransaction,
+ transition: IBinder,
+ taskInfo: ActivityManager.RunningTaskInfo,
+ ) {
+ if (!pipDesktopState.isDesktopWindowingPipEnabled()) {
+ return
+ }
+
+ // Early return if the transition is a synthetic transition that is not backed by a true
+ // system transition.
+ if (transition == DesktopTasksController.SYNTHETIC_TRANSITION) {
+ logD("handlePipTransitionIfInDesktop: SYNTHETIC_TRANSITION, not a true transition")
+ return
+ }
+
+ val taskId = taskInfo.taskId
+ val displayId = taskInfo.displayId
+ val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
+ if (!desktopRepository.isAnyDeskActive(displayId)) {
+ logD("handlePipTransitionIfInDesktop: PiP transition is not in Desktop session")
+ return
+ }
+
+ val deskId =
+ desktopRepository.getActiveDeskId(displayId)
+ ?: if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ logW(
+ "handlePipTransitionIfInDesktop: " +
+ "Active desk not found for display id %d",
+ displayId,
+ )
+ return
+ } else {
+ checkNotNull(desktopRepository.getDefaultDeskId(displayId)) {
+ "$TAG: handlePipTransitionIfInDesktop: " +
+ "Expected a default desk to exist in display with id $displayId"
+ }
+ }
+
+ val isLastTask =
+ desktopRepository.isOnlyVisibleNonClosingTaskInDesk(
+ taskId = taskId,
+ deskId = deskId,
+ displayId = displayId,
+ )
+ if (!isLastTask) {
+ logD("handlePipTransitionIfInDesktop: PiP task is not last visible task in Desk")
+ return
+ }
+
+ val desktopExitRunnable =
+ desktopTasksController.performDesktopExitCleanUp(
+ wct = wct,
+ deskId = deskId,
+ displayId = displayId,
+ willExitDesktop = true,
+ )
+ desktopExitRunnable?.invoke(transition)
+ }
+
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private fun logD(msg: String, vararg arguments: Any?) {
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private companion object {
+ private const val TAG = "DesktopPipTransitionController"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt
deleted file mode 100644
index efd3866e1bc4..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserver.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.desktopmode
-
-import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
-import android.os.IBinder
-import android.window.DesktopModeFlags
-import android.window.TransitionInfo
-import com.android.internal.protolog.ProtoLog
-import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
-
-/**
- * Observer of PiP in Desktop Mode transitions. At the moment, this is specifically tracking a PiP
- * transition for a task that is entering PiP via the minimize button on the caption bar.
- */
-class DesktopPipTransitionObserver {
- private val pendingPipTransitions = mutableMapOf<IBinder, PendingPipTransition>()
-
- /** Adds a pending PiP transition to be tracked. */
- fun addPendingPipTransition(transition: PendingPipTransition) {
- if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue) return
- pendingPipTransitions[transition.token] = transition
- }
-
- /**
- * Called when any transition is ready, which may include transitions not tracked by this
- * observer.
- */
- fun onTransitionReady(transition: IBinder, info: TransitionInfo) {
- if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue) return
- val pipTransition = pendingPipTransitions.remove(transition) ?: return
-
- logD("Desktop PiP transition ready: %s", transition)
- for (change in info.changes) {
- val taskInfo = change.taskInfo
- if (taskInfo == null || taskInfo.taskId == -1) {
- continue
- }
-
- if (
- taskInfo.taskId == pipTransition.taskId &&
- taskInfo.windowingMode == WINDOWING_MODE_PINNED
- ) {
- logD("Desktop PiP transition was successful")
- pipTransition.onSuccess()
- return
- }
- }
- logD("Change with PiP task not found in Desktop PiP transition; likely failed")
- }
-
- /**
- * Data tracked for a pending PiP transition.
- *
- * @property token the PiP transition that is started.
- * @property taskId task id of the task entering PiP.
- * @property onSuccess callback to be invoked if the PiP transition is successful.
- */
- data class PendingPipTransition(val token: IBinder, val taskId: Int, val onSuccess: () -> Unit)
-
- private fun logD(msg: String, vararg arguments: Any?) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
- }
-
- private companion object {
- private const val TAG = "DesktopPipTransitionObserver"
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index e77acfb805a6..25fdbb348356 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -1112,6 +1112,7 @@ class DesktopRepository(
internal fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopRepository")
+ pw.println("${innerPrefix}userId=$userId")
dumpDesktopTaskData(pw, innerPrefix)
pw.println("${innerPrefix}activeTasksListeners=${activeTasksListeners.size}")
pw.println("${innerPrefix}visibleTasksListeners=${visibleTasksListeners.size}")
@@ -1298,6 +1299,7 @@ class DesktopRepository(
deskByDisplayId[displayId]?.let { sequenceOf(it) } ?: emptySequence()
override fun remove(deskId: Int) {
+ setDeskInactive(deskId)
deskByDisplayId[deskId]?.clear()
}
@@ -1397,6 +1399,7 @@ class DesktopRepository(
desktopDisplays[displayId]?.orderedDesks?.asSequence() ?: emptySequence()
override fun remove(deskId: Int) {
+ setDeskInactive(deskId)
desktopDisplays.forEach { _, display ->
display.orderedDesks.removeIf { it.deskId == deskId }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 093e8ef8bc0e..2d6253b642be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -42,6 +42,7 @@ import android.os.Handler
import android.os.IBinder
import android.os.SystemProperties
import android.os.UserHandle
+import android.os.UserManager
import android.util.Slog
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
@@ -115,7 +116,6 @@ import com.android.wm.shell.desktopmode.multidesks.DeskTransition
import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.desktopmode.multidesks.DesksTransitionObserver
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
-import com.android.wm.shell.desktopmode.multidesks.createDesk
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer.DeskRecreationFactory
import com.android.wm.shell.draganddrop.DragAndDropController
@@ -163,6 +163,7 @@ import java.util.Optional
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
+import kotlin.coroutines.suspendCoroutine
import kotlin.jvm.optionals.getOrNull
/**
@@ -214,7 +215,6 @@ class DesktopTasksController(
private val overviewToDesktopTransitionObserver: OverviewToDesktopTransitionObserver,
private val desksOrganizer: DesksOrganizer,
private val desksTransitionObserver: DesksTransitionObserver,
- private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
private val userProfileContexts: UserProfileContexts,
private val desktopModeCompatPolicy: DesktopModeCompatPolicy,
private val dragToDisplayTransitionHandler: DragToDisplayTransitionHandler,
@@ -284,14 +284,8 @@ class DesktopTasksController(
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
desktopRepositoryInitializer.deskRecreationFactory =
- DeskRecreationFactory { deskUserId, destinationDisplayId, deskId ->
- if (deskUserId != userId) {
- // TODO: b/400984250 - add multi-user support for multi-desk restoration.
- logW("Tried to re-create desk of another user.")
- null
- } else {
- desksOrganizer.createDesk(destinationDisplayId)
- }
+ DeskRecreationFactory { deskUserId, destinationDisplayId, _ ->
+ createDeskSuspending(displayId = destinationDisplayId, userId = deskUserId)
}
}
}
@@ -449,6 +443,11 @@ class DesktopTasksController(
return false
}
+ // Secondary displays are always desktop-first
+ if (displayId != DEFAULT_DISPLAY) {
+ return true
+ }
+
val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
// A non-organized display (e.g., non-trusted virtual displays used in CTS) doesn't have
// TDA.
@@ -465,22 +464,79 @@ class DesktopTasksController(
return isFreeformDisplay
}
- /** Creates a new desk in the given display. */
- fun createDesk(displayId: Int) {
+ /** Called when the recents transition that started while in desktop is finishing. */
+ fun onRecentsInDesktopAnimationFinishing(
+ transition: IBinder,
+ finishWct: WindowContainerTransaction,
+ returnToApp: Boolean,
+ ) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ logV("onRecentsInDesktopAnimationFinishing returnToApp=%b", returnToApp)
+ if (returnToApp) return
+ // Home/Recents only exists in the default display.
+ val activeDesk = taskRepository.getActiveDeskId(DEFAULT_DISPLAY) ?: return
+ // Not going back to the active desk, deactivate it.
+ val runOnTransitStart =
+ performDesktopExitCleanUp(
+ wct = finishWct,
+ deskId = activeDesk,
+ displayId = DEFAULT_DISPLAY,
+ willExitDesktop = true,
+ shouldEndUpAtHome = true,
+ fromRecentsTransition = true,
+ )
+ runOnTransitStart?.invoke(transition)
+ }
+
+ /** Adds a new desk to the given display for the given user. */
+ fun createDesk(displayId: Int, userId: Int = this.userId) {
+ logV("addDesk displayId=%d, userId=%d", displayId, userId)
+ val repository = userRepositories.getProfile(userId)
+ createDesk(displayId, userId) { deskId ->
+ if (deskId == null) {
+ logW("Failed to add desk in displayId=%d for userId=%d", displayId, userId)
+ } else {
+ repository.addDesk(displayId = displayId, deskId = deskId)
+ }
+ }
+ }
+
+ private fun createDesk(displayId: Int, userId: Int = this.userId, onResult: (Int?) -> Unit) {
if (displayId == Display.INVALID_DISPLAY) {
logW("createDesk attempt with invalid displayId", displayId)
+ onResult(null)
return
}
- if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
- desksOrganizer.createDesk(displayId) { deskId ->
- taskRepository.addDesk(displayId = displayId, deskId = deskId)
- }
- } else {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
// In single-desk, the desk reuses the display id.
- taskRepository.addDesk(displayId = displayId, deskId = displayId)
+ logD("createDesk reusing displayId=%d for single-desk", displayId)
+ onResult(displayId)
+ return
+ }
+ if (
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_HSUM.isTrue &&
+ UserManager.isHeadlessSystemUserMode() &&
+ UserHandle.USER_SYSTEM == userId
+ ) {
+ logW("createDesk ignoring attempt for system user")
+ return
+ }
+ desksOrganizer.createDesk(displayId, userId) { deskId ->
+ logD(
+ "createDesk obtained deskId=%d for displayId=%d and userId=%d",
+ deskId,
+ displayId,
+ userId,
+ )
+ onResult(deskId)
}
}
+ private suspend fun createDeskSuspending(displayId: Int, userId: Int = this.userId): Int? =
+ suspendCoroutine { cont ->
+ createDesk(displayId, userId) { deskId -> cont.resumeWith(Result.success(deskId)) }
+ }
+
/** Moves task to desktop mode if task is running, else launches it in desktop mode. */
@JvmOverloads
fun moveTaskToDefaultDeskAndActivate(
@@ -818,7 +874,6 @@ class DesktopTasksController(
}
val isMinimizingToPip =
DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
- desktopPipTransitionObserver.isPresent &&
(taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false)
// If task is going to PiP, start a PiP transition instead of a minimize transition
@@ -832,23 +887,23 @@ class DesktopTasksController(
/* displayChange= */ null,
/* flags= */ 0,
)
- val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
+ val requestRes =
+ transitions.dispatchRequest(SYNTHETIC_TRANSITION, requestInfo, /* skip= */ null)
wct.merge(requestRes.second, true)
- desktopPipTransitionObserver.get().addPendingPipTransition(
- DesktopPipTransitionObserver.PendingPipTransition(
- token = freeformTaskTransitionStarter.startPipTransition(wct),
- taskId = taskInfo.taskId,
- onSuccess = {
- onDesktopTaskEnteredPip(
- taskId = taskId,
- deskId = deskId,
- displayId = taskInfo.displayId,
- taskIsLastVisibleTaskBeforePip = isLastTask,
- )
- },
- )
- )
+ // If the task minimizing to PiP is the last task, modify wct to perform Desktop cleanup
+ var desktopExitRunnable: RunOnTransitStart? = null
+ if (isLastTask) {
+ desktopExitRunnable =
+ performDesktopExitCleanUp(
+ wct = wct,
+ deskId = deskId,
+ displayId = displayId,
+ willExitDesktop = true,
+ )
+ }
+ val transition = freeformTaskTransitionStarter.startPipTransition(wct)
+ desktopExitRunnable?.invoke(transition)
} else {
snapEventHandler.removeTaskIfTiled(displayId, taskId)
val willExitDesktop = willExitDesktop(taskId, displayId, forceExitDesktop = false)
@@ -1114,6 +1169,7 @@ class DesktopTasksController(
}
val t =
if (remoteTransition == null) {
+ logV("startLaunchTransition -- no remoteTransition -- wct = $launchTransaction")
desktopMixedTransitionHandler.startLaunchTransition(
transitionType = transitionType,
wct = launchTransaction,
@@ -1208,6 +1264,7 @@ class DesktopTasksController(
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
launchBounds = bounds
+ launchDisplayId = displayId
if (DesktopModeFlags.ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX.isTrue) {
// Sets launch bounds size as flexible so core can recalculate.
flexibleLaunchSize = true
@@ -1860,11 +1917,7 @@ class DesktopTasksController(
displayId: Int,
forceExitDesktop: Boolean,
): Boolean {
- if (
- forceExitDesktop &&
- (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ||
- DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue)
- ) {
+ if (forceExitDesktop && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
// |forceExitDesktop| is true when the callers knows we'll exit desktop, such as when
// explicitly going fullscreen, so there's no point in checking the desktop state.
return true
@@ -1881,33 +1934,6 @@ class DesktopTasksController(
return true
}
- /** Potentially perform Desktop cleanup after a task successfully enters PiP. */
- @VisibleForTesting
- fun onDesktopTaskEnteredPip(
- taskId: Int,
- deskId: Int,
- displayId: Int,
- taskIsLastVisibleTaskBeforePip: Boolean,
- ) {
- if (
- !willExitDesktop(taskId, displayId, forceExitDesktop = taskIsLastVisibleTaskBeforePip)
- ) {
- return
- }
-
- val wct = WindowContainerTransaction()
- val desktopExitRunnable =
- performDesktopExitCleanUp(
- wct = wct,
- deskId = deskId,
- displayId = displayId,
- willExitDesktop = true,
- )
-
- val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
- desktopExitRunnable?.invoke(transition)
- }
-
private fun performDesktopExitCleanupIfNeeded(
taskId: Int,
deskId: Int? = null,
@@ -1931,22 +1957,29 @@ class DesktopTasksController(
}
/** TODO: b/394268248 - update [deskId] to be non-null. */
- private fun performDesktopExitCleanUp(
+ fun performDesktopExitCleanUp(
wct: WindowContainerTransaction,
deskId: Int?,
displayId: Int,
willExitDesktop: Boolean,
shouldEndUpAtHome: Boolean = true,
+ fromRecentsTransition: Boolean = false,
): RunOnTransitStart? {
if (!willExitDesktop) return null
desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
FULLSCREEN_ANIMATION_DURATION
)
- removeWallpaperActivity(wct, displayId)
- if (shouldEndUpAtHome) {
- // If the transition should end up with user going to home, launch home with a pending
- // intent.
- addLaunchHomePendingIntent(wct, displayId)
+ // No need to clean up the wallpaper / reorder home when coming from a recents transition.
+ if (
+ !fromRecentsTransition ||
+ !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ removeWallpaperActivity(wct, displayId)
+ if (shouldEndUpAtHome) {
+ // If the transition should end up with user going to home, launch home with a
+ // pending intent.
+ addLaunchHomePendingIntent(wct, displayId)
+ }
}
return prepareDeskDeactivationIfNeeded(wct, deskId)
}
@@ -2759,11 +2792,14 @@ class DesktopTasksController(
taskInfo: RunningTaskInfo,
deskId: Int?,
): RunOnTransitStart? {
- // This windowing mode is to get the transition animation started; once we complete
- // split select, we will change windowing mode to undefined and inherit from split stage.
- // Going to undefined here causes task to flicker to the top left.
- // Cancelling the split select flow will revert it to fullscreen.
- wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_MULTI_WINDOW)
+ if (!DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue) {
+ // This windowing mode is to get the transition animation started; once we complete
+ // split select, we will change windowing mode to undefined and inherit from split
+ // stage.
+ // Going to undefined here causes task to flicker to the top left.
+ // Cancelling the split select flow will revert it to fullscreen.
+ wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_MULTI_WINDOW)
+ }
// The task's density may have been overridden in freeform; revert it here as we don't
// want it overridden in multi-window.
wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
@@ -2858,7 +2894,7 @@ class DesktopTasksController(
* null and may be used to run other desktop policies, such as minimizing another task if the
* task limit has been exceeded.
*/
- fun addDeskActivationChanges(
+ private fun addDeskActivationChanges(
deskId: Int,
wct: WindowContainerTransaction,
newTask: TaskInfo? = null,
@@ -2916,6 +2952,8 @@ class DesktopTasksController(
}
}
}
+ val deactivatingDesk = taskRepository.getActiveDeskId(displayId)?.takeIf { it != deskId }
+ val deactivationRunnable = prepareDeskDeactivationIfNeeded(wct, deactivatingDesk)
return { transition ->
val activateDeskTransition =
if (newTaskIdInFront != null) {
@@ -2936,6 +2974,7 @@ class DesktopTasksController(
taskIdToMinimize?.let { minimizingTask ->
addPendingMinimizeTransition(transition, minimizingTask, MinimizeReason.TASK_LIMIT)
}
+ deactivationRunnable?.invoke(transition)
}
}
@@ -3013,18 +3052,17 @@ class DesktopTasksController(
}
val wct = WindowContainerTransaction()
- if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
- tasksToRemove.forEach {
- val task = shellTaskOrganizer.getRunningTaskInfo(it)
- if (task != null) {
- wct.removeTask(task.token)
- } else {
- recentTasksController?.removeBackgroundTask(it)
- }
+ tasksToRemove.forEach {
+ // TODO: b/404595635 - consider moving this block into [DesksOrganizer].
+ val task = shellTaskOrganizer.getRunningTaskInfo(it)
+ if (task != null) {
+ wct.removeTask(task.token)
+ } else {
+ recentTasksController?.removeBackgroundTask(it)
}
- } else {
- // TODO: 362720497 - double check background tasks are also removed.
- desksOrganizer.removeDesk(wct, deskId)
+ }
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ desksOrganizer.removeDesk(wct, deskId, userId)
}
if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue && wct.isEmpty) return
val transition = transitions.startTransition(TRANSIT_CLOSE, wct, /* handler= */ null)
@@ -3921,6 +3959,12 @@ class DesktopTasksController(
DesktopTaskToFrontReason.TASKBAR_MANAGE_WINDOW ->
UnminimizeReason.TASKBAR_MANAGE_WINDOW
}
+
+ @JvmField
+ /**
+ * A placeholder for a synthetic transition that isn't backed by a true system transition.
+ */
+ val SYNTHETIC_TRANSITION: IBinder = Binder()
}
/** Defines interface for classes that can listen to changes for task resize. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index df4d18f8c803..3fd955d112f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -42,7 +42,6 @@ import com.android.wm.shell.shared.TransitionUtil.isOpeningMode
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
-import java.util.Optional
/**
* A [Transitions.TransitionObserver] that observes shell transitions and updates the
@@ -55,7 +54,6 @@ class DesktopTasksTransitionObserver(
private val transitions: Transitions,
private val shellTaskOrganizer: ShellTaskOrganizer,
private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
- private val desktopPipTransitionObserver: Optional<DesktopPipTransitionObserver>,
private val backAnimationController: BackAnimationController,
private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
shellInit: ShellInit,
@@ -97,7 +95,6 @@ class DesktopTasksTransitionObserver(
removeTaskIfNeeded(info)
}
removeWallpaperOnLastTaskClosingIfNeeded(transition, info)
- desktopPipTransitionObserver.ifPresent { it.onTransitionReady(transition, info) }
}
private fun removeTaskIfNeeded(info: TransitionInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
index c10752d36bf9..72758bd538d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.content.pm.UserInfo
import android.os.UserManager
import android.util.SparseArray
+import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_HSUM
import androidx.core.util.forEach
import com.android.internal.protolog.ProtoLog
@@ -68,7 +69,10 @@ class DesktopUserRepositories(
if (DesktopModeStatus.canEnterDesktopMode(context)) {
shellInit.addInitCallback(::onInit, this)
}
- if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
+ if (
+ ENABLE_DESKTOP_WINDOWING_HSUM.isTrue() &&
+ !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
}
}
@@ -76,6 +80,13 @@ class DesktopUserRepositories(
private fun onInit() {
repositoryInitializer.initialize(this)
shellController.addUserChangeListener(this)
+ if (
+ ENABLE_DESKTOP_WINDOWING_HSUM.isTrue() &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ userId = ActivityManager.getCurrentUser()
+ userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
+ }
}
/** Returns [DesktopRepository] for the parent user id. */
@@ -97,10 +108,10 @@ class DesktopUserRepositories(
/** Dumps [DesktopRepository] for each user. */
fun dump(pw: PrintWriter, prefix: String) {
- desktopRepoByUserId.forEach { key, value ->
- pw.println("${prefix}userId=$key")
- value.dump(pw, prefix)
- }
+ val innerPrefix = "$prefix "
+ pw.println("${prefix}DesktopUserRepositories:")
+ pw.println("${innerPrefix}currentUserId=$userId")
+ desktopRepoByUserId.forEach { key, value -> value.dump(pw, innerPrefix) }
}
override fun onUserChanged(newUserId: Int, userContext: Context) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
index 8ce624e103ef..bf687f2e4f3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.PackageManager
import android.os.Handler
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.sysui.ShellInit
@@ -28,6 +29,7 @@ import java.util.function.Supplier
/**
* This supplies the package name of default home in an efficient way. The query to package manager
* only executes on initialization and when the preferred activity (e.g. default home) is changed.
+ * Note that this returns null package name if the default home is setup wizard.
*/
class DefaultHomePackageSupplier(
private val context: Context,
@@ -36,6 +38,7 @@ class DefaultHomePackageSupplier(
) : BroadcastReceiver(), Supplier<String?> {
private var defaultHomePackage: String? = null
+ private var isSetupWizard: Boolean = false
init {
shellInit.addInitCallback({ onInit() }, this)
@@ -52,6 +55,14 @@ class DefaultHomePackageSupplier(
private fun updateDefaultHomePackage(): String? {
defaultHomePackage = context.packageManager.getHomeActivities(ArrayList())?.packageName
+ isSetupWizard =
+ defaultHomePackage != null &&
+ context.packageManager.resolveActivity(
+ Intent()
+ .setPackage(defaultHomePackage)
+ .addCategory(Intent.CATEGORY_SETUP_WIZARD),
+ PackageManager.MATCH_SYSTEM_ONLY,
+ ) != null
return defaultHomePackage
}
@@ -60,6 +71,7 @@ class DefaultHomePackageSupplier(
}
override fun get(): String? {
+ if (isSetupWizard) return null
return defaultHomePackage ?: updateDefaultHomePackage()
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
index 9dec96933ee5..454419c805c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
@@ -18,9 +18,12 @@ package com.android.wm.shell.desktopmode.multidesks
import android.os.IBinder
/** Represents shell-started transitions involving desks. */
-sealed class DeskTransition {
+sealed interface DeskTransition {
/** The transition token. */
- abstract val token: IBinder
+ val token: IBinder
+
+ /** Returns a copy of this desk transition with a new transition token. */
+ fun copyWithToken(token: IBinder): DeskTransition
/** A transition to remove a desk and its tasks from a display. */
data class RemoveDesk(
@@ -29,11 +32,15 @@ sealed class DeskTransition {
val deskId: Int,
val tasks: Set<Int>,
val onDeskRemovedListener: OnDeskRemovedListener?,
- ) : DeskTransition()
+ ) : DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
/** A transition to activate a desk in its display. */
data class ActivateDesk(override val token: IBinder, val displayId: Int, val deskId: Int) :
- DeskTransition()
+ DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
/** A transition to activate a desk by moving an outside task to it. */
data class ActiveDeskWithTask(
@@ -41,8 +48,12 @@ sealed class DeskTransition {
val displayId: Int,
val deskId: Int,
val enterTaskId: Int,
- ) : DeskTransition()
+ ) : DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
/** A transition to deactivate a desk. */
- data class DeactivateDesk(override val token: IBinder, val deskId: Int) : DeskTransition()
+ data class DeactivateDesk(override val token: IBinder, val deskId: Int) : DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
index 5a988fcd1b77..605465b15468 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
@@ -18,13 +18,11 @@ package com.android.wm.shell.desktopmode.multidesks
import android.app.ActivityManager
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
-import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer.OnCreateCallback
-import kotlin.coroutines.suspendCoroutine
/** An organizer of desk containers in which to host child desktop windows. */
interface DesksOrganizer {
- /** Creates a new desk container in the given display. */
- fun createDesk(displayId: Int, callback: OnCreateCallback)
+ /** Creates a new desk container to use in the given display for the given user. */
+ fun createDesk(displayId: Int, userId: Int, callback: OnCreateCallback)
/** Activates the given desk, making it visible in its display. */
fun activateDesk(wct: WindowContainerTransaction, deskId: Int)
@@ -32,8 +30,8 @@ interface DesksOrganizer {
/** Deactivates the given desk, removing it as the default launch container for new tasks. */
fun deactivateDesk(wct: WindowContainerTransaction, deskId: Int)
- /** Removes the given desk and its desktop windows. */
- fun removeDesk(wct: WindowContainerTransaction, deskId: Int)
+ /** Removes the given desk of the given user. */
+ fun removeDesk(wct: WindowContainerTransaction, deskId: Int, userId: Int)
/** Moves the given task to the given desk. */
fun moveTaskToDesk(
@@ -78,15 +76,12 @@ interface DesksOrganizer {
/** Whether the desk is activate according to the given change at the end of a transition. */
fun isDeskActiveAtEnd(change: TransitionInfo.Change, deskId: Int): Boolean
+ /** Allows for other classes to respond to task changes this organizer receives. */
+ fun setOnDesktopTaskInfoChangedListener(listener: (ActivityManager.RunningTaskInfo) -> Unit)
+
/** A callback that is invoked when the desk container is created. */
fun interface OnCreateCallback {
/** Calls back when the [deskId] has been created. */
fun onCreated(deskId: Int)
}
}
-
-/** Creates a new desk container in the given display. */
-suspend fun DesksOrganizer.createDesk(displayId: Int): Int = suspendCoroutine { cont ->
- val onCreateCallback = OnCreateCallback { deskId -> cont.resumeWith(Result.success(deskId)) }
- createDesk(displayId, onCreateCallback)
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
index b521b2e8c942..588b5c350330 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
@@ -39,6 +39,7 @@ class DesksTransitionObserver(
val transitions = deskTransitions[transition.token] ?: mutableSetOf()
transitions += transition
deskTransitions[transition.token] = transitions
+ logD("Added pending desk transition: %s", transition)
}
/**
@@ -51,6 +52,43 @@ class DesksTransitionObserver(
deskTransitions.forEach { deskTransition -> handleDeskTransition(info, deskTransition) }
}
+ /**
+ * Called when a transition is merged with another transition, which may include transitions not
+ * tracked by this observer.
+ */
+ fun onTransitionMerged(merged: IBinder, playing: IBinder) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ val transitions = deskTransitions.remove(merged) ?: return
+ deskTransitions[playing] =
+ transitions
+ .map { deskTransition -> deskTransition.copyWithToken(token = playing) }
+ .toMutableSet()
+ }
+
+ /**
+ * Called when any transition finishes, which may include transitions not tracked by this
+ * observer.
+ *
+ * Most [DeskTransition]s are not handled here because [onTransitionReady] handles them and
+ * removes them from the map. However, there can be cases where the transition was added after
+ * [onTransitionReady] had already been called and they need to be handled here, such as the
+ * swipe-to-home recents transition when there is no book-end transition.
+ */
+ fun onTransitionFinished(transition: IBinder) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ val deskTransitions = deskTransitions.remove(transition) ?: return
+ deskTransitions.forEach { deskTransition ->
+ if (deskTransition is DeskTransition.DeactivateDesk) {
+ handleDeactivateDeskTransition(null, deskTransition)
+ } else {
+ logW(
+ "Unexpected desk transition finished without being handled: %s",
+ deskTransition,
+ )
+ }
+ }
+ }
+
private fun handleDeskTransition(info: TransitionInfo, deskTransition: DeskTransition) {
logD("Desk transition ready: %s", deskTransition)
val desktopRepository = desktopUserRepositories.current
@@ -102,41 +140,54 @@ class DesksTransitionObserver(
)
}
}
- is DeskTransition.DeactivateDesk -> {
- var visibleDeactivation = false
- for (change in info.changes) {
- val isDeskChange = desksOrganizer.isDeskChange(change, deskTransition.deskId)
- if (isDeskChange) {
- visibleDeactivation = true
- continue
- }
- val taskId = change.taskInfo?.taskId ?: continue
- val removedFromDesk =
- desktopRepository.getDeskIdForTask(taskId) == deskTransition.deskId &&
- desksOrganizer.getDeskAtEnd(change) == null
- if (removedFromDesk) {
- desktopRepository.removeTaskFromDesk(
- deskId = deskTransition.deskId,
- taskId = taskId,
- )
- }
- }
- // Always deactivate even if there's no change that confirms the desk was
- // deactivated. Some interactions, such as the desk deactivating because it's
- // occluded by a fullscreen task result in a transition change, but others, such
- // as transitioning from an empty desk to home may not.
- if (!visibleDeactivation) {
- logD("Deactivating desk without transition change")
- }
- desktopRepository.setDeskInactive(deskId = deskTransition.deskId)
+ is DeskTransition.DeactivateDesk -> handleDeactivateDeskTransition(info, deskTransition)
+ }
+ }
+
+ private fun handleDeactivateDeskTransition(
+ info: TransitionInfo?,
+ deskTransition: DeskTransition.DeactivateDesk,
+ ) {
+ logD("handleDeactivateDeskTransition: %s", deskTransition)
+ val desktopRepository = desktopUserRepositories.current
+ var deskChangeFound = false
+
+ val changes = info?.changes ?: emptyList()
+ for (change in changes) {
+ val isDeskChange = desksOrganizer.isDeskChange(change, deskTransition.deskId)
+ if (isDeskChange) {
+ deskChangeFound = true
+ continue
+ }
+ val taskId = change.taskInfo?.taskId ?: continue
+ val removedFromDesk =
+ desktopRepository.getDeskIdForTask(taskId) == deskTransition.deskId &&
+ desksOrganizer.getDeskAtEnd(change) == null
+ if (removedFromDesk) {
+ desktopRepository.removeTaskFromDesk(
+ deskId = deskTransition.deskId,
+ taskId = taskId,
+ )
}
}
+ // Always deactivate even if there's no change that confirms the desk was
+ // deactivated. Some interactions, such as the desk deactivating because it's
+ // occluded by a fullscreen task result in a transition change, but others, such
+ // as transitioning from an empty desk to home may not.
+ if (!deskChangeFound) {
+ logD("Deactivating desk without transition change")
+ }
+ desktopRepository.setDeskInactive(deskId = deskTransition.deskId)
}
private fun logD(msg: String, vararg arguments: Any?) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
private companion object {
private const val TAG = "DesksTransitionObserver"
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
index 49ca58e7b32a..e4edeb95be6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
@@ -30,6 +30,7 @@ import android.window.TransitionInfo
import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import androidx.core.util.forEach
+import androidx.core.util.valueIterator
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
@@ -40,7 +41,13 @@ import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellInit
import java.io.PrintWriter
-/** A [DesksOrganizer] that uses root tasks as the container of each desk. */
+/**
+ * A [DesksOrganizer] that uses root tasks as the container of each desk.
+ *
+ * Note that root tasks are reusable between multiple users at the same time, and may also be
+ * pre-created to have one ready for the first entry to the default desk, so root-task existence
+ * does not imply a formal desk exists to the user.
+ */
class RootTaskDesksOrganizer(
shellInit: ShellInit,
shellCommandHandler: ShellCommandHandler,
@@ -54,6 +61,7 @@ class RootTaskDesksOrganizer(
mutableListOf<CreateDeskMinimizationRootRequest>()
@VisibleForTesting
val deskMinimizationRootsByDeskId: MutableMap<Int, DeskMinimizationRoot> = mutableMapOf()
+ private var onTaskInfoChangedListener: ((RunningTaskInfo) -> Unit)? = null
init {
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
@@ -64,9 +72,26 @@ class RootTaskDesksOrganizer(
}
}
- override fun createDesk(displayId: Int, callback: OnCreateCallback) {
- logV("createDesk in display: %d", displayId)
- createDeskRootRequests += CreateDeskRequest(displayId, callback)
+ override fun createDesk(displayId: Int, userId: Int, callback: OnCreateCallback) {
+ logV("createDesk in displayId=%d userId=%s", displayId, userId)
+ // Find an existing desk that is not yet used by this user.
+ val unassignedDesk =
+ deskRootsByDeskId
+ .valueIterator()
+ .asSequence()
+ .filterNot { desk -> userId in desk.users }
+ .firstOrNull()
+ if (unassignedDesk != null) {
+ unassignedDesk.users.add(userId)
+ callback.onCreated(unassignedDesk.deskId)
+ return
+ }
+ createDeskRoot(displayId, userId, callback)
+ }
+
+ private fun createDeskRoot(displayId: Int, userId: Int, callback: OnCreateCallback) {
+ logV("createDeskRoot in display: %d for user: %d", displayId, userId)
+ createDeskRootRequests += CreateDeskRequest(displayId, userId, callback)
shellTaskOrganizer.createRootTask(
displayId,
WINDOWING_MODE_FREEFORM,
@@ -75,31 +100,52 @@ class RootTaskDesksOrganizer(
)
}
- override fun removeDesk(wct: WindowContainerTransaction, deskId: Int) {
- logV("removeDesk %d", deskId)
- deskRootsByDeskId[deskId]?.let { root -> wct.removeRootTask(root.token) }
- deskMinimizationRootsByDeskId[deskId]?.let { root -> wct.removeRootTask(root.token) }
+ override fun removeDesk(wct: WindowContainerTransaction, deskId: Int, userId: Int) {
+ logV("removeDesk %d for userId=%d", deskId, userId)
+ val deskRoot = deskRootsByDeskId[deskId]
+ if (deskRoot == null) {
+ logW("removeDesk attempted to remove non-existent desk=%d", deskId)
+ return
+ }
+ updateLaunchRoot(wct, deskId, enabled = false)
+ deskRoot.users.remove(userId)
+ if (deskRoot.users.isEmpty()) {
+ // No longer in use by any users, remove it completely.
+ logD("removeDesk %d is no longer used by any users, removing it completely", deskId)
+ wct.removeRootTask(deskRoot.token)
+ deskMinimizationRootsByDeskId[deskId]?.let { root -> wct.removeRootTask(root.token) }
+ }
}
override fun activateDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("activateDesk %d", deskId)
val root = checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
wct.reorder(root.token, /* onTop= */ true)
- wct.setLaunchRoot(
- /* container= */ root.taskInfo.token,
- /* windowingModes= */ intArrayOf(WINDOWING_MODE_FREEFORM, WINDOWING_MODE_UNDEFINED),
- /* activityTypes= */ intArrayOf(ACTIVITY_TYPE_UNDEFINED, ACTIVITY_TYPE_STANDARD),
- )
+ updateLaunchRoot(wct, deskId, enabled = true)
}
override fun deactivateDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("deactivateDesk %d", deskId)
+ updateLaunchRoot(wct, deskId, enabled = false)
+ }
+
+ private fun updateLaunchRoot(wct: WindowContainerTransaction, deskId: Int, enabled: Boolean) {
val root = checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
- wct.setLaunchRoot(
- /* container= */ root.taskInfo.token,
- /* windowingModes= */ null,
- /* activityTypes= */ null,
- )
+ root.isLaunchRootRequested = enabled
+ logD("updateLaunchRoot deskId=%d enabled=%b", deskId, enabled)
+ if (enabled) {
+ wct.setLaunchRoot(
+ /* container= */ root.taskInfo.token,
+ /* windowingModes= */ intArrayOf(WINDOWING_MODE_FREEFORM, WINDOWING_MODE_UNDEFINED),
+ /* activityTypes= */ intArrayOf(ACTIVITY_TYPE_UNDEFINED, ACTIVITY_TYPE_STANDARD),
+ )
+ } else {
+ wct.setLaunchRoot(
+ /* container= */ root.taskInfo.token,
+ /* windowingModes= */ null,
+ /* activityTypes= */ null,
+ )
+ }
}
override fun moveTaskToDesk(
@@ -213,6 +259,10 @@ class RootTaskDesksOrganizer(
change.taskInfo?.isVisibleRequested == true &&
change.mode == TRANSIT_TO_FRONT
+ override fun setOnDesktopTaskInfoChangedListener(listener: (RunningTaskInfo) -> Unit) {
+ onTaskInfoChangedListener = listener
+ }
+
override fun onTaskAppeared(taskInfo: RunningTaskInfo, leash: SurfaceControl) {
handleTaskAppeared(taskInfo, leash)
updateLaunchAdjacentController()
@@ -220,6 +270,12 @@ class RootTaskDesksOrganizer(
override fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
handleTaskInfoChanged(taskInfo)
+ if (
+ taskInfo.taskId !in deskRootsByDeskId &&
+ deskMinimizationRootsByDeskId.values.none { it.rootId == taskInfo.taskId }
+ ) {
+ onTaskInfoChangedListener?.invoke(taskInfo)
+ }
updateLaunchAdjacentController()
}
@@ -264,7 +320,13 @@ class RootTaskDesksOrganizer(
// Appearing root matches desk request.
val deskId = taskInfo.taskId
logV("Desk #$deskId appeared")
- deskRootsByDeskId[deskId] = DeskRoot(deskId, taskInfo, leash)
+ deskRootsByDeskId[deskId] =
+ DeskRoot(
+ deskId = deskId,
+ taskInfo = taskInfo,
+ leash = leash,
+ users = mutableSetOf(deskRequest.userId),
+ )
createDeskRootRequests.remove(deskRequest)
deskRequest.onCreateCallback.onCreated(deskId)
createDeskMinimizationRoot(displayId = appearingInDisplayId, deskId = deskId)
@@ -419,6 +481,8 @@ class RootTaskDesksOrganizer(
val taskInfo: RunningTaskInfo,
val leash: SurfaceControl,
val children: MutableSet<Int> = mutableSetOf(),
+ val users: MutableSet<Int> = mutableSetOf(),
+ var isLaunchRootRequested: Boolean = false,
) {
val token: WindowContainerToken = taskInfo.token
}
@@ -438,15 +502,24 @@ class RootTaskDesksOrganizer(
private data class CreateDeskRequest(
val displayId: Int,
+ val userId: Int,
val onCreateCallback: OnCreateCallback,
)
private data class CreateDeskMinimizationRootRequest(val displayId: Int, val deskId: Int)
+ private fun logD(msg: String, vararg arguments: Any?) {
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
private fun logV(msg: String, vararg arguments: Any?) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
private fun logE(msg: String, vararg arguments: Any?) {
ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
@@ -462,7 +535,9 @@ class RootTaskDesksOrganizer(
val minimizationRoot = deskMinimizationRootsByDeskId[deskId]
pw.println("$innerPrefix #$deskId visible=${root.taskInfo.isVisible}")
pw.println("$innerPrefix displayId=${root.taskInfo.displayId}")
+ pw.println("$innerPrefix isLaunchRootRequested=${root.isLaunchRootRequested}")
pw.println("$innerPrefix children=${root.children}")
+ pw.println("$innerPrefix users=${root.users}")
pw.println("$innerPrefix minimization root:")
pw.println("$innerPrefix rootId=${minimizationRoot?.rootId}")
if (minimizationRoot != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index b3c1a92f5e1d..d6084138b7e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -663,6 +663,9 @@ public class DragLayout extends LinearLayout
mSession = null;
}
});
+ // notify bubbles of drag cancel
+ mCurrentBubbleBarTarget = null;
+ mBubbleBarDragListener.onItemDraggedOutsideBubbleBarDropZone();
// Reset the state if we previously force-ignore the bottom margin
mDropZoneView1.setForceIgnoreBottomMargin(false);
mDropZoneView2.setForceIgnoreBottomMargin(false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 0bf2ea61b0a4..2476ee1a4090 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -174,7 +174,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
SurfaceControl.Transaction finishT) {
mTaskChangeListener.ifPresent(listener -> listener.onTaskChanging(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
private void onToFrontTransitionReady(
@@ -184,7 +184,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
mTaskChangeListener.ifPresent(
listener -> listener.onTaskMovingToFront(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
private void onToBackTransitionReady(
@@ -194,7 +194,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
mTaskChangeListener.ifPresent(
listener -> listener.onTaskMovingToBack(change.getTaskInfo()));
mWindowDecorViewModel.onTaskChanging(
- change.getTaskInfo(), change.getLeash(), startT, finishT, change.getMode());
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@Override
@@ -207,6 +207,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
@Override
public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
+ mDesksTransitionObserver.ifPresent(o -> o.onTransitionMerged(merged, playing));
if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
// TODO(b/367268953): Remove when DesktopTaskListener is introduced.
mDesktopImmersiveController.ifPresent(h -> h.onTransitionMerged(merged, playing));
@@ -232,6 +233,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
@Override
public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {
+ mDesksTransitionObserver.ifPresent(o -> o.onTransitionFinished(transition));
if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
// TODO(b/367268953): Remove when DesktopTaskListener is introduced.
mDesktopImmersiveController.ifPresent(h -> h.onTransitionFinished(transition, aborted));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 5d8d8b685a23..9ec1c7d65a6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -50,7 +50,9 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Debug;
import android.os.IBinder;
+import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -71,6 +73,7 @@ import com.android.wm.shell.common.pip.PipDesktopState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.desktopmode.DesktopPipTransitionController;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
@@ -115,6 +118,7 @@ public class PipTransition extends PipTransitionController implements
private final DisplayController mDisplayController;
private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
private final PipDesktopState mPipDesktopState;
+ private final Optional<DesktopPipTransitionController> mDesktopPipTransitionController;
private final PipInteractionHandler mPipInteractionHandler;
//
@@ -158,6 +162,7 @@ public class PipTransition extends PipTransitionController implements
DisplayController displayController,
Optional<SplitScreenController> splitScreenControllerOptional,
PipDesktopState pipDesktopState,
+ Optional<DesktopPipTransitionController> desktopPipTransitionController,
PipInteractionHandler pipInteractionHandler) {
super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
pipBoundsAlgorithm);
@@ -172,6 +177,7 @@ public class PipTransition extends PipTransitionController implements
mDisplayController = displayController;
mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(mContext);
mPipDesktopState = pipDesktopState;
+ mDesktopPipTransitionController = desktopPipTransitionController;
mPipInteractionHandler = pipInteractionHandler;
mExpandHandler = new PipExpandHandler(mContext, pipBoundsState, pipBoundsAlgorithm,
@@ -227,7 +233,18 @@ public class PipTransition extends PipTransitionController implements
@NonNull TransitionRequestInfo request) {
if (isAutoEnterInButtonNavigation(request) || isEnterPictureInPictureModeRequest(request)) {
mEnterTransition = transition;
- return getEnterPipTransaction(transition, request.getPipChange());
+ final WindowContainerTransaction wct = getEnterPipTransaction(transition,
+ request.getPipChange());
+
+ mDesktopPipTransitionController.ifPresent(
+ desktopPipTransitionController ->
+ desktopPipTransitionController.handlePipTransition(
+ wct,
+ transition,
+ request.getPipChange().getTaskInfo()
+ )
+ );
+ return wct;
}
return null;
}
@@ -300,6 +317,20 @@ public class PipTransition extends PipTransitionController implements
return startAlphaTypeEnterAnimation(info, startTransaction, finishTransaction,
finishCallback);
}
+
+ TransitionInfo.Change pipActivityChange = PipTransitionUtils
+ .getDeferConfigActivityChange(info, pipChange.getTaskInfo().getToken());
+ if (pipActivityChange == null) {
+ // Legacy-enter and swipe-pip-to-home filters did not resolve a scheduled PiP entry.
+ // Bounds-type enter animation is the last resort, and it requires a config-at-end
+ // activity amongst the list of changes. If no such change, something went wrong.
+ Log.wtf(TAG, String.format("""
+ PipTransition.startAnimation didn't handle a scheduled PiP entry
+ transitionInfo=%s,
+ callers=%s""", info, Debug.getCallers(4)));
+ return false;
+ }
+
return startBoundsTypeEnterAnimation(info, startTransaction, finishTransaction,
finishCallback);
} else if (transition == mExitViaExpandTransition) {
@@ -824,26 +855,15 @@ public class PipTransition extends PipTransitionController implements
return true;
}
- // Sometimes root PiP task can have TF children. These child containers can be collected
- // even if they can promote to their parents: e.g. if they are marked as "organized".
- // So we count the chain of containers under PiP task as one "real" changing target;
- // iterate through changes bottom-to-top to properly identify parents.
- int expectedTargetCount = 1;
- WindowContainerToken lastPipChildToken = pipChange.getContainer();
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- TransitionInfo.Change change = info.getChanges().get(i);
- if (change == pipChange || change.getContainer() == null) continue;
- if (change.getParent() != null && change.getParent().equals(lastPipChildToken)) {
- // Allow an extra change since our pinned root task has a child.
- ++expectedTargetCount;
- lastPipChildToken = change.getContainer();
- }
- }
-
- // If the only root task change in the changes list is a opening type PiP task,
- // then this is legacy-enter PiP.
- return info.getChanges().size() == expectedTargetCount
- && TransitionUtil.isOpeningMode(pipChange.getMode());
+ // #getEnterPipTransaction() always attempts to mark PiP activity as config-at-end one.
+ // However, the activity will only actually be marked config-at-end by Core if it is
+ // both isVisible and isVisibleRequested, which is when we can't run bounds animation.
+ //
+ // So we can use the absence of a config-at-end activity as a signal that we should run
+ // a legacy-enter PiP animation instead.
+ return TransitionUtil.isOpeningMode(pipChange.getMode())
+ && PipTransitionUtils.getDeferConfigActivityChange(
+ info, pipChange.getContainer()) == null;
}
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 014c810d1e5f..10db5ca03637 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -124,7 +124,6 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.widget.Toast;
import android.window.DesktopExperienceFlags;
-import android.window.DesktopModeFlags;
import android.window.DisplayAreaInfo;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
@@ -684,8 +683,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!enteredSplitSelect) {
return null;
}
- if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()
- && !DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
mTaskOrganizer.applyTransaction(wct);
return null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index a6f872634ee9..22848c38bb1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -728,6 +728,48 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
taskView.notifyAppeared(newTask);
}
+ /**
+ * Updates bounds for the task view during an unfold transition.
+ *
+ * @return true if the task was found and a transition for this task is pending. false
+ * otherwise.
+ */
+ public boolean updateBoundsForUnfold(Rect bounds, SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ final TaskViewTaskController taskView = findTaskView(taskInfo);
+ if (taskView == null) {
+ return false;
+ }
+
+ final PendingTransition pendingTransition = findPending(taskView, TRANSIT_CHANGE);
+ if (pendingTransition == null) {
+ return false;
+ }
+
+ mPending.remove(pendingTransition);
+
+ // reparent the task under the task view surface and set the bounds on it
+ startTransaction.reparent(leash, taskView.getSurfaceControl())
+ .setPosition(leash, 0, 0)
+ .setWindowCrop(leash, bounds.width(), bounds.height())
+ .show(leash);
+ // the finish transaction would reparent the task back to the transition root, so reparent
+ // it again to the task view surface
+ finishTransaction.reparent(leash, taskView.getSurfaceControl())
+ .setPosition(leash, 0, 0)
+ .setWindowCrop(leash, bounds.width(), bounds.height());
+ if (useRepo()) {
+ final TaskViewRepository.TaskViewState state = mTaskViewRepo.byTaskView(taskView);
+ if (state != null) {
+ state.mBounds.set(bounds);
+ }
+ } else {
+ updateBoundsState(taskView, bounds);
+ }
+ return true;
+ }
+
private void updateBounds(TaskViewTaskController taskView, Rect boundsOnScreen,
SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index a0fb62508cc1..e19ad23b2125 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -394,6 +394,9 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
((RecentsMixedTransition) mixed).onAnimateRecentsDuringSplitFinishing(
returnToApp, finishWct, finishT);
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
+ ((RecentsMixedTransition) mixed).onAnimateRecentsDuringDesktopFinishing(
+ returnToApp, finishWct);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
index ce98b03b77a6..bff08ba6d88f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
@@ -42,9 +42,10 @@ public class DefaultSurfaceAnimator {
@NonNull Animation anim, @NonNull SurfaceControl leash,
@NonNull Runnable finishCallback, @NonNull TransactionPool pool,
@NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
- @Nullable Rect clipRect) {
+ @Nullable Rect clipRect,
+ @Nullable TransitionAnimationHelper.RoundedContentPerDisplay roundedBounds) {
final DefaultAnimationAdapter adapter = new DefaultAnimationAdapter(anim, leash,
- position, clipRect, cornerRadius);
+ position, clipRect, cornerRadius, roundedBounds);
buildSurfaceAnimation(animations, anim, finishCallback, pool, mainExecutor, adapter);
}
@@ -109,9 +110,17 @@ public class DefaultSurfaceAnimator {
@Nullable final Rect mClipRect;
@Nullable private final Rect mAnimClipRect;
final float mCornerRadius;
+ final int mWindowBottom;
+
+ /**
+ * Inset changes aren't synchronized with transitions, so use a "provider" to track the
+ * bottom of the display content during the animation.
+ */
+ @Nullable final TransitionAnimationHelper.RoundedContentPerDisplay mRoundedContentBounds;
DefaultAnimationAdapter(@NonNull Animation anim, @NonNull SurfaceControl leash,
- @Nullable Point position, @Nullable Rect clipRect, float cornerRadius) {
+ @Nullable Point position, @Nullable Rect clipRect, float cornerRadius,
+ TransitionAnimationHelper.RoundedContentPerDisplay roundedBounds) {
super(leash);
mAnim = anim;
mPosition = (position != null && (position.x != 0 || position.y != 0))
@@ -119,6 +128,8 @@ public class DefaultSurfaceAnimator {
mClipRect = (clipRect != null && !clipRect.isEmpty()) ? clipRect : null;
mAnimClipRect = mClipRect != null ? new Rect() : null;
mCornerRadius = cornerRadius;
+ mWindowBottom = clipRect != null ? clipRect.bottom : 0;
+ mRoundedContentBounds = roundedBounds;
}
@Override
@@ -136,6 +147,11 @@ public class DefaultSurfaceAnimator {
if (mClipRect != null) {
boolean needCrop = false;
+ if (mRoundedContentBounds != null) {
+ mClipRect.bottom = Math.min(mRoundedContentBounds.mBounds.bottom,
+ mWindowBottom);
+ }
+
mAnimClipRect.set(mClipRect);
if (transformation.hasClipRect()) {
mAnimClipRect.intersectUnchecked(transformation.getClipRect());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index bf5800330979..5b6993863c5d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -59,13 +59,13 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static com.android.internal.jank.Cuj.CUJ_DEFAULT_TASK_TO_TASK_ANIMATION;
-import static com.android.internal.policy.TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CHANGE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+import static com.android.wm.shell.Flags.enableDynamicInsetsForAppLaunch;
import static com.android.wm.shell.transition.DefaultSurfaceAnimator.buildSurfaceAnimation;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
@@ -111,11 +111,13 @@ import com.android.window.flags.Flags;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.animation.SizeChangeAnimation;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.TransitionUtil;
+import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.sysui.ShellInit;
import java.util.ArrayList;
@@ -125,15 +127,18 @@ import java.util.function.Consumer;
/** The default handler that handles anything not already handled. */
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private static final int MAX_ANIMATION_DURATION = 3000;
+ private static final int SIZE_CHANGE_ANIMATION_DURATION = 400;
private final TransactionPool mTransactionPool;
private final DisplayController mDisplayController;
private final Context mContext;
private final Handler mMainHandler;
+ private final Handler mAnimHandler;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionAnimation mTransitionAnimation;
private final DevicePolicyManager mDevicePolicyManager;
+ private final TransitionAnimationHelper.RoundedContentTracker mRoundedContentBounds;
/** Keeps track of the currently-running animations associated with each transition. */
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
@@ -163,9 +168,11 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
DefaultTransitionHandler(@NonNull Context context,
@NonNull ShellInit shellInit,
@NonNull DisplayController displayController,
+ @NonNull DisplayInsetsController displayInsetsController,
@NonNull TransactionPool transactionPool,
@NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor,
+ @NonNull Handler animHandler,
@NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer,
@NonNull InteractionJankMonitor interactionJankMonitor) {
mDisplayController = displayController;
@@ -174,11 +181,14 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
mMainHandler = mainHandler;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mAnimHandler = animHandler;
mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
mCurrentUserId = UserHandle.myUserId();
mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
shellInit.addInitCallback(this::onInit, this);
mRootTDAOrganizer = rootTDAOrganizer;
+ mRoundedContentBounds = new TransitionAnimationHelper.RoundedContentTracker(
+ displayController, displayInsetsController);
mInteractionJankMonitor = interactionJankMonitor;
}
@@ -191,6 +201,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
mMainHandler);
TransitionAnimation.initAttributeCache(mContext, mMainHandler);
+ mRoundedContentBounds.init();
}
private void updateEnterpriseThumbnailDrawable() {
@@ -297,6 +308,18 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
return ROTATION_ANIMATION_SEAMLESS;
}
+ @Nullable
+ final TransitionAnimationHelper.RoundedContentPerDisplay getRoundedContentBounds(
+ TransitionInfo.Change change) {
+ if (!enableDynamicInsetsForAppLaunch()) {
+ return null;
+ }
+ if (change.getTaskInfo() == null && change.getActivityComponent() == null) {
+ return null;
+ }
+ return mRoundedContentBounds.forDisplay(change.getEndDisplayId());
+ }
+
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -329,10 +352,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
mAnimations.put(transition, animations);
final boolean isTaskTransition = isTaskTransition(info);
- if (isTaskTransition) {
- mInteractionJankMonitor.begin(info.getRoot(0).getLeash(), mContext,
- mMainHandler, CUJ_DEFAULT_TASK_TO_TASK_ANIMATION);
- }
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
@@ -556,7 +575,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
change.getEndAbsBounds().left - animRoot.getOffset().x,
change.getEndAbsBounds().top - animRoot.getOffset().y);
- if (change.getActivityComponent() != null) {
+ final boolean isActivity = change.getActivityComponent() != null;
+ if (isActivity) {
// For appcompat letterbox: we intentionally report the task-bounds so that we
// can animate as-if letterboxes are "part of" the activity. This means we can't
// always rely solely on endAbsBounds and need to also max with endRelOffset.
@@ -564,7 +584,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
animRelOffset.y = Math.max(animRelOffset.y, change.getEndRelOffset().y);
}
- if (change.getActivityComponent() != null && !isActivityLevel
+ if (isActivity && !isActivityLevel
&& !mRotator.isRotated(change)) {
// At this point, this is an independent activity change in a non-activity
// transition. This means that an activity transition got erroneously combined
@@ -589,8 +609,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
buildSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
- mTransactionPool, mMainExecutor, animRelOffset, cornerRadius,
- clipRect);
+ mTransactionPool, mMainExecutor, animRelOffset, cornerRadius, clipRect,
+ isTask || isActivity
+ ? mRoundedContentBounds.forDisplay(change.getEndDisplayId())
+ : null);
final TransitionInfo.AnimationOptions options = change.getAnimationOptions();
if (options != null) {
@@ -619,6 +641,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
// now start animations. they are started on another thread, so we have to post them
// *after* applying the startTransaction
mAnimExecutor.execute(() -> {
+ if (isTaskTransition) {
+ mInteractionJankMonitor.begin(info.getRoot(0).getLeash(), mContext,
+ mAnimHandler, CUJ_DEFAULT_TASK_TO_TASK_ANIMATION);
+ }
for (int i = 0; i < animations.size(); ++i) {
animations.get(i).start();
}
@@ -779,15 +805,16 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private void startBoundsChangeAnimation(@NonNull SurfaceControl.Transaction startT,
@NonNull ArrayList<Animator> animations, @NonNull TransitionInfo.Change change,
@NonNull Runnable finishCb, @NonNull ShellExecutor mainExecutor) {
- final SizeChangeAnimation sca =
- new SizeChangeAnimation(change.getStartAbsBounds(), change.getEndAbsBounds());
+ final SizeChangeAnimation sca = new SizeChangeAnimation(change.getStartAbsBounds(),
+ change.getEndAbsBounds(), /* initialScale= */ 1f, /* scaleFactor= */ 1f);
sca.initialize(change.getLeash(), change.getSnapshot(), startT);
final ValueAnimator va = sca.buildAnimator(change.getLeash(), change.getSnapshot(),
(animator) -> mainExecutor.execute(() -> {
animations.remove(animator);
finishCb.run();
}));
- va.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+ va.setDuration(SIZE_CHANGE_ANIMATION_DURATION);
+ va.setInterpolator(Interpolators.EMPHASIZED);
animations.add(va);
}
@@ -932,7 +959,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
+ mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds(),
+ getRoundedContentBounds(change));
}
private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@@ -956,7 +984,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
+ mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds(),
+ getRoundedContentBounds(change));
}
private static int getWallpaperTransitType(TransitionInfo info) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index cca982142a3a..c71458dec5ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -29,15 +29,18 @@ import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
import android.os.IBinder;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.shared.IHomeTransitionListener;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
+import com.android.wm.shell.sysui.ShellInit;
/**
* The {@link TransitionObserver} that observes for transitions involving the home
@@ -51,13 +54,30 @@ public class HomeTransitionObserver implements TransitionObserver,
private @NonNull final Context mContext;
private @NonNull final ShellExecutor mMainExecutor;
+ private @NonNull final DisplayInsetsController mDisplayInsetsController;
private IBinder mPendingStartDragTransition;
private Boolean mPendingHomeVisibilityUpdate;
public HomeTransitionObserver(@NonNull Context context,
- @NonNull ShellExecutor mainExecutor) {
+ @NonNull ShellExecutor mainExecutor,
+ @NonNull DisplayInsetsController displayInsetsController,
+ @NonNull ShellInit shellInit) {
mContext = context;
mMainExecutor = mainExecutor;
+ mDisplayInsetsController = displayInsetsController;
+
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY,
+ new DisplayInsetsController.OnInsetsChangedListener() {
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ if (mListener == null) return;
+ mListener.call(l -> l.onDisplayInsetsChanged(insetsState));
+ }
+ });
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index 1e926c57ca61..cfefc1f1ac66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -198,6 +198,15 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition {
}
}
+ /**
+ * Called when the recents animation during desktop is about to finish.
+ */
+ void onAnimateRecentsDuringDesktopFinishing(boolean returnToApp,
+ @NonNull WindowContainerTransaction finishWct) {
+ mDesktopTasksController.onRecentsInDesktopAnimationFinishing(mTransition, finishWct,
+ returnToApp);
+ }
+
@Override
void mergeAnimation(
@NonNull IBinder transition, @NonNull TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index aa42b7f0ca76..8100f1d1a9a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -347,21 +347,21 @@ class ScreenRotationAnimation {
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateEnterAnimation, getEnterSurface(), finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */);
+ null /* clipRect */, null);
}
private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */);
+ null /* clipRect */, null);
}
private void buildScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */);
+ null /* clipRect */, null);
}
private void buildLumaAnimation(@NonNull ArrayList<Animator> animations,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index edfb56019a60..48b48640a37f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -39,6 +39,11 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.util.SparseArray;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -47,6 +52,9 @@ import android.window.TransitionInfo;
import com.android.internal.R;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransitionUtil;
@@ -325,4 +333,77 @@ public class TransitionAnimationHelper {
}
return false;
}
+
+ /**
+ * In some situations (eg. TaskBar) the content area of a display appears to be rounded. For
+ * these situations, we may want the animation to also express the same rounded corners (even
+ * though in steady-state, the app internally manages the insets). This class Keeps track of,
+ * and provides, the bounds of rounded-corner display content.
+ *
+ * This is used to enable already-running animations to adapt to changes in taskbar/navbar
+ * position live.
+ */
+ public static class RoundedContentPerDisplay implements
+ DisplayInsetsController.OnInsetsChangedListener {
+
+ /** The current bounds of the display content (post-inset). */
+ final Rect mBounds = new Rect();
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ Insets insets = Insets.NONE;
+ for (int i = insetsState.sourceSize() - 1; i >= 0; i--) {
+ final InsetsSource source = insetsState.sourceAt(i);
+ if (!source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) {
+ continue;
+ }
+ insets = Insets.max(source.calculateInsets(insetsState.getDisplayFrame(), false),
+ insets);
+ }
+ mBounds.set(insetsState.getDisplayFrame());
+ mBounds.inset(insets);
+ }
+ }
+
+ /**
+ * Keeps track of the bounds of rounded-corner display content (post-inset).
+ *
+ * @see RoundedContentPerDisplay
+ */
+ public static class RoundedContentTracker implements
+ DisplayController.OnDisplaysChangedListener {
+ final DisplayController mDisplayController;
+ final DisplayInsetsController mDisplayInsetsController;
+ final SparseArray<RoundedContentPerDisplay> mPerDisplay = new SparseArray<>();
+
+ RoundedContentTracker(DisplayController dc, DisplayInsetsController dic) {
+ mDisplayController = dc;
+ mDisplayInsetsController = dic;
+ }
+
+ void init() {
+ mDisplayController.addDisplayWindowListener(this);
+ }
+
+ RoundedContentPerDisplay forDisplay(int displayId) {
+ return mPerDisplay.get(displayId);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ final RoundedContentPerDisplay perDisplay = new RoundedContentPerDisplay();
+ mDisplayInsetsController.addInsetsChangedListener(displayId, perDisplay);
+ mPerDisplay.put(displayId, perDisplay);
+ final DisplayLayout dl = mDisplayController.getDisplayLayout(displayId);
+ perDisplay.mBounds.set(0, 0, dl.width(), dl.height());
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ final RoundedContentPerDisplay listener = mPerDisplay.removeReturnOld(displayId);
+ if (listener != null) {
+ mDisplayInsetsController.removeInsetsChangedListener(displayId, listener);
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 4f49ebcd2e83..84724268cfc2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -84,6 +84,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
@@ -214,6 +215,7 @@ public class Transitions implements RemoteCallable<Transitions>,
private final Context mContext;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
+ private final Handler mAnimHandler;
private final TransitionPlayerImpl mPlayerImpl;
private final DefaultTransitionHandler mDefaultTransitionHandler;
private final RemoteTransitionHandler mRemoteTransitionHandler;
@@ -314,14 +316,16 @@ public class Transitions implements RemoteCallable<Transitions>,
@NonNull ShellTaskOrganizer organizer,
@NonNull TransactionPool pool,
@NonNull DisplayController displayController,
+ @NonNull DisplayInsetsController displayInsetsController,
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor,
+ @NonNull Handler animHandler,
@NonNull HomeTransitionObserver homeTransitionObserver,
@NonNull FocusTransitionObserver focusTransitionObserver) {
this(context, shellInit, new ShellCommandHandler(), shellController, organizer, pool,
- displayController, mainExecutor, mainHandler, animExecutor,
- new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit),
+ displayController, displayInsetsController, mainExecutor, mainHandler, animExecutor,
+ animHandler, new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit),
homeTransitionObserver, focusTransitionObserver);
}
@@ -332,9 +336,11 @@ public class Transitions implements RemoteCallable<Transitions>,
@NonNull ShellTaskOrganizer organizer,
@NonNull TransactionPool pool,
@NonNull DisplayController displayController,
+ @NonNull DisplayInsetsController displayInsetsController,
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor,
+ @NonNull Handler animHandler,
@NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer,
@NonNull HomeTransitionObserver homeTransitionObserver,
@NonNull FocusTransitionObserver focusTransitionObserver) {
@@ -342,11 +348,12 @@ public class Transitions implements RemoteCallable<Transitions>,
mContext = context;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mAnimHandler = animHandler;
mDisplayController = displayController;
mPlayerImpl = new TransitionPlayerImpl();
mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit,
- displayController, pool, mainExecutor, mainHandler, animExecutor, rootTDAOrganizer,
- InteractionJankMonitor.getInstance());
+ displayController, displayInsetsController, pool, mainExecutor, mainHandler,
+ animExecutor, mAnimHandler, rootTDAOrganizer, InteractionJankMonitor.getInstance());
mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor);
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index 7fd19a7d2a88..706a366441cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -38,6 +38,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.bubbles.BubbleTaskUnfoldTransitionMerger;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.sysui.ShellInit;
@@ -53,6 +54,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.Executor;
/**
@@ -80,6 +82,7 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
private final Transitions mTransitions;
+ private final Optional<BubbleTaskUnfoldTransitionMerger> mBubbleTaskUnfoldTransitionMerger;
private final Executor mExecutor;
private final TransactionPool mTransactionPool;
private final Handler mHandler;
@@ -108,12 +111,14 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
TransactionPool transactionPool,
Executor executor,
Handler handler,
- Transitions transitions) {
+ Transitions transitions,
+ Optional<BubbleTaskUnfoldTransitionMerger> bubbleTaskUnfoldTransitionMerger) {
mUnfoldProgressProvider = unfoldProgressProvider;
mTransitions = transitions;
mTransactionPool = transactionPool;
mExecutor = executor;
mHandler = handler;
+ mBubbleTaskUnfoldTransitionMerger = bubbleTaskUnfoldTransitionMerger;
mAnimators.add(splitUnfoldTaskAnimator);
mAnimators.add(fullscreenUnfoldAnimator);
@@ -237,14 +242,26 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
}
// TODO (b/286928742) unfold transition handler should be part of mixed handler to
// handle merges better.
+
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo != null
&& taskInfo.configuration.windowConfiguration.isAlwaysOnTop()) {
- // Tasks that are always on top (e.g. bubbles), will handle their own transition
- // as they are on top of everything else. So skip merging transitions here.
- return;
+ // Tasks that are always on top, excluding bubbles, will handle their own transition
+ // as they are on top of everything else. If this is a transition for a bubble task,
+ // attempt to merge it. Otherwise skip merging transitions.
+ if (mBubbleTaskUnfoldTransitionMerger.isPresent()) {
+ boolean merged =
+ mBubbleTaskUnfoldTransitionMerger
+ .get()
+ .mergeTaskWithUnfold(taskInfo, change, startT, finishT);
+ if (!merged) {
+ return;
+ }
+ } else {
+ return;
+ }
}
}
// Apply changes happening during the unfold animation immediately
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 42321e56e72b..7871179a50de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -49,7 +49,6 @@ import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewConfiguration;
import android.window.DisplayAreaInfo;
-import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -234,8 +233,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT,
- @TransitionInfo.TransitionMode int changeMode) {
+ SurfaceControl.Transaction finishT) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
index 4511fbe10764..2b2cdf84005c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
@@ -31,7 +31,6 @@ import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.SurfaceControl;
import android.view.View;
-import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -160,8 +159,7 @@ public abstract class CarWindowDecorViewModel
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT,
- @TransitionInfo.TransitionMode int changeMode) {
+ SurfaceControl.Transaction finishT) {
final CarWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
index f6acca95916f..dc1b94e80ed7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
@@ -89,9 +89,6 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
updateRelayoutParams(mRelayoutParams, taskInfo, isCaptionVisible);
relayout(mRelayoutParams, startT, finishT, wct, mRootView, mResult);
- if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
- setCaptionVisibility(isCaptionVisible);
- }
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
@@ -100,19 +97,23 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
// Nothing is set up in this case including the decoration surface.
return;
}
+
if (mRootView != mResult.mRootView) {
mRootView = mResult.mRootView;
setupRootView(mResult.mRootView, mClickListener);
}
- }
- private void setCaptionVisibility(boolean visible) {
- if (mRootView == null) {
- return;
+ if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
+ setCaptionVisibility(mRootView, mRelayoutParams.mIsCaptionVisible);
}
+ }
+
+ private void setCaptionVisibility(@NonNull View rootView, boolean visible) {
final int v = visible ? View.VISIBLE : View.GONE;
- final View captionView = mRootView.findViewById(getCaptionViewId());
- captionView.setVisibility(v);
+ final View captionView = rootView.findViewById(getCaptionViewId());
+ if (captionView != null) {
+ captionView.setVisibility(v);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 800faca830f4..16fa5120d64b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -27,7 +27,6 @@ import static android.view.MotionEvent.ACTION_HOVER_EXIT;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.statusBars;
-import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
@@ -80,7 +79,6 @@ import android.view.ViewConfiguration;
import android.view.ViewRootImpl;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshot;
-import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -108,6 +106,8 @@ import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.compatui.api.CompatUIHandler;
+import com.android.wm.shell.compatui.impl.CompatUIRequests;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
@@ -125,6 +125,7 @@ import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction;
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeUtilsKt;
import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
import com.android.wm.shell.desktopmode.education.AppToWebEducationController;
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.recents.RecentsTransitionStateListener;
@@ -210,6 +211,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final AppHandleAndHeaderVisibilityHelper mAppHandleAndHeaderVisibilityHelper;
private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
private final AppHandleViewHolder.Factory mAppHandleViewHolderFactory;
+ private final DesksOrganizer mDesksOrganizer;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -266,6 +268,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final DesktopTilingDecorViewModel mDesktopTilingDecorViewModel;
private final MultiDisplayDragMoveIndicatorController mMultiDisplayDragMoveIndicatorController;
private final LatencyTracker mLatencyTracker;
+ private final CompatUIHandler mCompatUI;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -306,7 +309,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
- MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
+ CompatUIHandler compatUI,
+ DesksOrganizer desksOrganizer) {
this(
context,
shellExecutor,
@@ -353,7 +358,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
recentsTransitionHandler,
desktopModeCompatPolicy,
desktopTilingDecorViewModel,
- multiDisplayDragMoveIndicatorController);
+ multiDisplayDragMoveIndicatorController,
+ compatUI,
+ desksOrganizer);
}
@VisibleForTesting
@@ -403,7 +410,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
- MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
+ CompatUIHandler compatUI,
+ DesksOrganizer desksOrganizer) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -444,6 +453,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mActivityOrientationChangeHandler = activityOrientationChangeHandler;
mAssistContentRequester = assistContentRequester;
mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
+ mCompatUI = compatUI;
mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> {
DesktopModeWindowDecoration decoration;
RunningTaskInfo taskInfo;
@@ -480,6 +490,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mDesktopTasksController.setSnapEventHandler(this);
mMultiDisplayDragMoveIndicatorController = multiDisplayDragMoveIndicatorController;
mLatencyTracker = LatencyTracker.getInstance(mContext);
+ mDesksOrganizer = desksOrganizer;
shellInit.addInitCallback(this::onInit, this);
}
@@ -518,6 +529,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
});
}
mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+ mDesksOrganizer.setOnDesktopTaskInfoChangedListener((taskInfo) -> {
+ onTaskInfoChanged(taskInfo);
+ return Unit.INSTANCE;
+ });
}
@Override
@@ -595,8 +610,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT,
- @TransitionInfo.TransitionMode int changeMode) {
+ SurfaceControl.Transaction finishT) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (!shouldShowWindowDecor(taskInfo)) {
if (decoration != null) {
@@ -610,8 +624,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
} else {
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
- mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion,
- /*isMovingToBack= */ changeMode == TRANSIT_TO_BACK);
+ mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion);
}
}
@@ -626,7 +639,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion, /* isMovingToBack= */ false);
+ mExclusionRegion);
}
@Override
@@ -1795,6 +1808,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mMultiInstanceHelper,
mWindowDecorCaptionHandleRepository,
mDesktopModeEventLogger,
+ mDesktopModeUiEventLogger,
mDesktopModeCompatPolicy);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
@@ -1864,6 +1878,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
CompatUIController.launchUserAspectRatioSettings(mContext, taskInfo);
return Unit.INSTANCE;
});
+ windowDecoration.setOnRestartClickListener(() -> {
+ mCompatUI.sendCompatUIRequest(new CompatUIRequests.DisplayCompatShowRestartDialog(
+ taskInfo.taskId));
+ return Unit.INSTANCE;
+ });
windowDecoration.setOnMaximizeHoverListener(() -> {
if (!windowDecoration.isMaximizeMenuActive()) {
mDesktopModeUiEventLogger.log(taskInfo,
@@ -1879,7 +1898,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo),
- mExclusionRegion, /* isMovingToBack= */ false);
+ mExclusionRegion);
if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) {
incrementEventReceiverTasks(taskInfo.displayId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index ae103895d56b..d24308137936 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -93,6 +93,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.CaptionState;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeUtils;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
@@ -163,6 +164,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private Function0<Unit> mOnNewWindowClickListener;
private Function0<Unit> mOnManageWindowsClickListener;
private Function0<Unit> mOnChangeAspectRatioClickListener;
+ private Function0<Unit> mOnRestartClickListener;
private Function0<Unit> mOnMaximizeHoverListener;
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
@@ -210,11 +212,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopUserRepositories mDesktopUserRepositories;
+ private final DesktopModeUiEventLogger mDesktopModeUiEventLogger;
private boolean mIsRecentsTransitionRunning = false;
private boolean mIsDragging = false;
private Runnable mLoadAppInfoRunnable;
private Runnable mSetAppInfoRunnable;
- private boolean mIsMovingToBack;
public DesktopModeWindowDecoration(
Context context,
@@ -242,6 +244,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopModeCompatPolicy desktopModeCompatPolicy) {
this (context, userContext, displayController, taskResourceLoader, splitScreenController,
desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
@@ -256,7 +259,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
DefaultMaximizeMenuFactory.INSTANCE,
DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper,
windowDecorCaptionHandleRepository, desktopModeEventLogger,
- desktopModeCompatPolicy);
+ desktopModeUiEventLogger, desktopModeCompatPolicy);
}
DesktopModeWindowDecoration(
@@ -293,6 +296,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopModeCompatPolicy desktopModeCompatPolicy) {
super(context, userContext, displayController, taskOrganizer, taskInfo,
taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
@@ -320,6 +324,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mTaskResourceLoader = taskResourceLoader;
mTaskResourceLoader.onWindowDecorCreated(taskInfo);
mDesktopModeCompatPolicy = desktopModeCompatPolicy;
+ mDesktopModeUiEventLogger = desktopModeUiEventLogger;
}
/**
@@ -408,6 +413,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnChangeAspectRatioClickListener = listener;
}
+ /** Registers a listener to be called when the aspect ratio action is triggered. */
+ void setOnRestartClickListener(Function0<Unit> listener) {
+ mOnRestartClickListener = listener;
+ }
+
/** Registers a listener to be called when the maximize header button is hovered. */
void setOnMaximizeHoverListener(Function0<Unit> listener) {
mOnMaximizeHoverListener = listener;
@@ -468,7 +478,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
// causes flickering. See b/270202228.
final boolean applyTransactionOnDraw = taskInfo.isFreeform();
relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
- hasGlobalFocus, displayExclusionRegion, mIsMovingToBack);
+ hasGlobalFocus, displayExclusionRegion);
if (!applyTransactionOnDraw) {
t.apply();
}
@@ -495,8 +505,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
- boolean hasGlobalFocus, @NonNull Region displayExclusionRegion,
- boolean isMovingToBack) {
+ boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
Trace.beginSection("DesktopModeWindowDecoration#relayout");
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_APP_TO_WEB.isTrue()) {
@@ -519,7 +528,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final boolean inFullImmersive = mDesktopUserRepositories.getProfile(taskInfo.userId)
.isTaskInFullImmersiveState(taskInfo.taskId);
- mIsMovingToBack = isMovingToBack;
updateRelayoutParams(mRelayoutParams, mContext, taskInfo, mSplitScreenController,
applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive,
@@ -528,8 +536,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
/* shouldIgnoreCornerRadius= */ mIsRecentsTransitionRunning
&& DesktopModeFlags
.ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue(),
- mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo),
- mIsRecentsTransitionRunning, mIsMovingToBack);
+ mDesktopModeCompatPolicy.shouldExcludeCaptionFromAppBounds(taskInfo));
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -622,6 +629,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mBgExecutor.execute(mLoadAppInfoRunnable);
}
+ private boolean showInputLayer() {
+ if (!DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
+ return isCaptionVisible();
+ }
+ // Don't show the input layer during the recents transition, otherwise it could become
+ // touchable while in overview, during quick-switch or even for a short moment after going
+ // Home.
+ return isCaptionVisible() && !mIsRecentsTransitionRunning;
+ }
+
private boolean isCaptionVisible() {
return mTaskInfo.isVisible && mIsCaptionVisible;
}
@@ -863,7 +880,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
if (!isAppHandle(mWindowDecorViewHolder)) return;
asAppHandle(mWindowDecorViewHolder).bindData(new AppHandleViewHolder.HandleData(
mTaskInfo, determineHandlePosition(), mResult.mCaptionWidth,
- mResult.mCaptionHeight, /* showInputLayer= */ isCaptionVisible(),
+ mResult.mCaptionHeight, /* showInputLayer= */ showInputLayer(),
/* isCaptionVisible= */ isCaptionVisible()
));
}
@@ -889,10 +906,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
mWindowManagerWrapper,
- mHandler
+ mHandler,
+ mDesktopModeUiEventLogger
);
- } else if (mRelayoutParams.mLayoutResId
- == R.layout.desktop_mode_app_header) {
+ } else if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_header) {
return mAppHeaderViewHolderFactory.create(
mResult.mRootView,
mOnCaptionTouchListener,
@@ -902,7 +919,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnLeftSnapClickListener,
mOnRightSnapClickListener,
mOnMaximizeOrRestoreClickListener,
- mOnMaximizeHoverListener);
+ mOnMaximizeHoverListener,
+ mDesktopModeUiEventLogger);
}
throw new IllegalArgumentException("Unexpected layout resource id");
}
@@ -947,9 +965,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
boolean hasGlobalFocus,
@NonNull Region displayExclusionRegion,
boolean shouldIgnoreCornerRadius,
- boolean shouldExcludeCaptionFromAppBounds,
- boolean isRecentsTransitionRunning,
- boolean isMovingToBack) {
+ boolean shouldExcludeCaptionFromAppBounds) {
final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
final boolean isAppHeader =
captionLayoutId == R.layout.desktop_mode_app_header;
@@ -967,19 +983,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
relayoutParams.mAsyncViewHost = isAppHandle;
boolean showCaption;
- // If this relayout is occurring from an observed TRANSIT_TO_BACK transition, do not
- // show caption (this includes split select transition).
- if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
- && isMovingToBack && !isDragging) {
- showCaption = false;
- } else if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_IMMERSIVE_DRAG_BUGFIX.isTrue() && isDragging) {
// If the task is being dragged, the caption should not be hidden so that it continues
// receiving input
showCaption = true;
- } else if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()
- && isRecentsTransitionRunning) {
- // Caption should not be visible in recents.
- showCaption = false;
} else if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
if (inFullImmersiveMode) {
showCaption = (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
@@ -1366,7 +1373,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mMaximizeMenu = mMaximizeMenuFactory.create(mSyncQueue, mRootTaskDisplayAreaOrganizer,
mDisplayController, mTaskInfo, mContext,
(width, height) -> calculateMaximizeMenuPosition(width, height),
- mSurfaceControlTransactionSupplier);
+ mSurfaceControlTransactionSupplier, mDesktopModeUiEventLogger);
mMaximizeMenu.show(
/* isTaskInImmersiveMode= */
@@ -1461,6 +1468,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
&& mMinimumInstancesFound;
final boolean shouldShowChangeAspectRatioButton = HandleMenu.Companion
.shouldShowChangeAspectRatioButton(mTaskInfo);
+ final boolean shouldShowRestartButton = HandleMenu.Companion
+ .shouldShowRestartButton(mTaskInfo);
final boolean inDesktopImmersive = mDesktopUserRepositories.getProfile(mTaskInfo.userId)
.isTaskInFullImmersiveState(mTaskInfo.taskId);
final boolean isBrowserApp = isBrowserApp();
@@ -1477,8 +1486,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
shouldShowManageWindowsButton,
shouldShowChangeAspectRatioButton,
isDesktopModeSupportedOnDisplay(mContext, mDisplay),
+ shouldShowRestartButton,
isBrowserApp,
isBrowserApp ? getAppLink() : getBrowserLink(),
+ mDesktopModeUiEventLogger,
mResult.mCaptionWidth,
mResult.mCaptionHeight,
mResult.mCaptionX,
@@ -1513,6 +1524,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
return Unit.INSTANCE;
},
+ /* onRestartClickListener= */ mOnRestartClickListener,
/* onCloseMenuClickListener= */ () -> {
closeHandleMenu();
return Unit.INSTANCE;
@@ -1878,18 +1890,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* <p> When a Recents transition is active we allow that transition to take ownership of the
* corner radius of its task surfaces, so each window decoration should stop updating the corner
* radius of its task surface during that time.
- *
- * We should not allow input to reach the input layer during a Recents transition, so
- * update the handle view holder accordingly if transition status changes.
*/
void setIsRecentsTransitionRunning(boolean isRecentsTransitionRunning) {
- if (mIsRecentsTransitionRunning != isRecentsTransitionRunning) {
- mIsRecentsTransitionRunning = isRecentsTransitionRunning;
- if (DesktopModeFlags.ENABLE_INPUT_LAYER_TRANSITION_FIX.isTrue()) {
- // We don't relayout decor on recents transition, so we need to call it directly.
- relayout(mTaskInfo, mHasGlobalFocus, mRelayoutParams.mDisplayExclusionRegion);
- }
- }
+ mIsRecentsTransitionRunning = isRecentsTransitionRunning;
}
/**
@@ -1963,6 +1966,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopModeCompatPolicy desktopModeCompatPolicy) {
return new DesktopModeWindowDecoration(
context,
@@ -1990,6 +1994,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
multiInstanceHelper,
windowDecorCaptionHandleRepository,
desktopModeEventLogger,
+ desktopModeUiEventLogger,
desktopModeCompatPolicy);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 8a8bdcadd67a..97a47c602bcd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -435,7 +435,9 @@ class DragResizeInputListener implements AutoCloseable {
}
// Removing this surface on the background thread to ensure that mInitInputChannels has
// already been finished.
- mSurfaceControlTransactionSupplier.get().remove(mDecorationSurface).apply();
+ // Do not |remove| the surface, the decoration might still be needed even if
+ // drag-resizing isn't.
+ mDecorationSurface.release();
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index 5f13ba907831..71bb153e4b1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -28,6 +28,7 @@ import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
+import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_OUTSIDE
@@ -35,6 +36,7 @@ import android.view.SurfaceControl
import android.view.View
import android.view.WindowInsets.Type.systemBars
import android.view.WindowManager
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.Space
@@ -48,10 +50,14 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit
import androidx.core.view.isGone
import com.android.window.flags.Flags
import com.android.wm.shell.R
-import com.android.wm.shell.bubbles.ContextUtils.isRtl
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_DESKTOP_VIEW
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_FULLSCREEN
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_SPLIT_SCREEN
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
+import com.android.wm.shell.shared.bubbles.ContextUtils.isRtl
import com.android.wm.shell.shared.split.SplitScreenConstants
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
@@ -94,8 +100,10 @@ class HandleMenu(
private val shouldShowManageWindowsButton: Boolean,
private val shouldShowChangeAspectRatioButton: Boolean,
private val shouldShowDesktopModeButton: Boolean,
+ private val shouldShowRestartButton: Boolean,
private val isBrowserApp: Boolean,
private val openInAppOrBrowserIntent: Intent?,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
private val captionWidth: Int,
private val captionHeight: Int,
captionX: Int,
@@ -138,7 +146,8 @@ class HandleMenu(
private val shouldShowMoreActionsPill: Boolean
get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
- shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
+ shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton ||
+ shouldShowRestartButton
private var loadAppInfoJob: Job? = null
@@ -156,6 +165,7 @@ class HandleMenu(
onChangeAspectRatioClickListener: () -> Unit,
openInAppOrBrowserClickListener: (Intent) -> Unit,
onOpenByDefaultClickListener: () -> Unit,
+ onRestartClickListener: () -> Unit,
onCloseMenuClickListener: () -> Unit,
onOutsideTouchListener: () -> Unit,
forceShowSystemBars: Boolean = false,
@@ -175,6 +185,7 @@ class HandleMenu(
onChangeAspectRatioClickListener = onChangeAspectRatioClickListener,
openInAppOrBrowserClickListener = openInAppOrBrowserClickListener,
onOpenByDefaultClickListener = onOpenByDefaultClickListener,
+ onRestartClickListener = onRestartClickListener,
onCloseMenuClickListener = onCloseMenuClickListener,
onOutsideTouchListener = onOutsideTouchListener,
forceShowSystemBars = forceShowSystemBars,
@@ -197,12 +208,14 @@ class HandleMenu(
onChangeAspectRatioClickListener: () -> Unit,
openInAppOrBrowserClickListener: (Intent) -> Unit,
onOpenByDefaultClickListener: () -> Unit,
+ onRestartClickListener: () -> Unit,
onCloseMenuClickListener: () -> Unit,
onOutsideTouchListener: () -> Unit,
forceShowSystemBars: Boolean = false,
) {
val handleMenuView = HandleMenuView(
context = context,
+ desktopModeUiEventLogger = desktopModeUiEventLogger,
menuWidth = menuWidth,
captionHeight = captionHeight,
shouldShowWindowingPill = shouldShowWindowingPill,
@@ -211,6 +224,7 @@ class HandleMenu(
shouldShowManageWindowsButton = shouldShowManageWindowsButton,
shouldShowChangeAspectRatioButton = shouldShowChangeAspectRatioButton,
shouldShowDesktopModeButton = shouldShowDesktopModeButton,
+ shouldShowRestartButton = shouldShowRestartButton,
isBrowserApp = isBrowserApp
).apply {
bind(taskInfo, shouldShowMoreActionsPill)
@@ -224,6 +238,7 @@ class HandleMenu(
this.onOpenInAppOrBrowserClickListener = {
openInAppOrBrowserClickListener.invoke(openInAppOrBrowserIntent!!)
}
+ this.onRestartClickListener = onRestartClickListener
this.onOpenByDefaultClickListener = onOpenByDefaultClickListener
this.onCloseMenuClickListener = onCloseMenuClickListener
this.onOutsideTouchListener = onOutsideTouchListener
@@ -430,6 +445,10 @@ class HandleMenu(
R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height
)
}
+ if (!shouldShowRestartButton) {
+ menuHeight -= loadDimensionPixelSize(
+ R.dimen.desktop_mode_handle_menu_restart_button_height)
+ }
if (!shouldShowMoreActionsPill) {
menuHeight -= pillTopMargin
}
@@ -464,6 +483,7 @@ class HandleMenu(
@SuppressLint("ClickableViewAccessibility")
class HandleMenuView(
private val context: Context,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
menuWidth: Int,
captionHeight: Int,
private val shouldShowWindowingPill: Boolean,
@@ -472,6 +492,7 @@ class HandleMenu(
private val shouldShowManageWindowsButton: Boolean,
private val shouldShowChangeAspectRatioButton: Boolean,
private val shouldShowDesktopModeButton: Boolean,
+ private val shouldShowRestartButton: Boolean,
private val isBrowserApp: Boolean
) {
val rootView = LayoutInflater.from(context)
@@ -487,6 +508,9 @@ class HandleMenu(
private val iconButtonRippleRadius = context.resources.getDimensionPixelSize(
R.dimen.desktop_mode_handle_menu_icon_button_ripple_radius
)
+ private val handleMenuCornerRadius = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_handle_menu_corner_radius
+ )
private val iconButtonDrawableInsetsBase = DrawableInsets(
t = iconButtondrawableBaseInset,
b = iconButtondrawableBaseInset, l = iconButtondrawableBaseInset,
@@ -549,6 +573,8 @@ class HandleMenu(
.requireViewById<HandleMenuActionButton>(R.id.manage_windows_button)
private val changeAspectRatioBtn = moreActionsPill
.requireViewById<HandleMenuActionButton>(R.id.change_aspect_ratio_button)
+ private val restartBtn = moreActionsPill
+ .requireViewById<HandleMenuActionButton>(R.id.handle_menu_restart_button)
// Open in Browser/App Pill.
private val openInAppOrBrowserPill = rootView.requireViewById<View>(
@@ -574,6 +600,7 @@ class HandleMenu(
var onChangeAspectRatioClickListener: (() -> Unit)? = null
var onOpenInAppOrBrowserClickListener: (() -> Unit)? = null
var onOpenByDefaultClickListener: (() -> Unit)? = null
+ var onRestartClickListener: (() -> Unit)? = null
var onCloseMenuClickListener: (() -> Unit)? = null
var onOutsideTouchListener: (() -> Unit)? = null
@@ -590,6 +617,7 @@ class HandleMenu(
newWindowBtn.setOnClickListener { onNewWindowClickListener?.invoke() }
manageWindowBtn.setOnClickListener { onManageWindowsClickListener?.invoke() }
changeAspectRatioBtn.setOnClickListener { onChangeAspectRatioClickListener?.invoke() }
+ restartBtn.setOnClickListener { onRestartClickListener?.invoke() }
rootView.setOnTouchListener { _, event ->
if (event.actionMasked == ACTION_OUTSIDE) {
@@ -599,6 +627,45 @@ class HandleMenu(
return@setOnTouchListener true
}
+ desktopBtn.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_DESKTOP_VIEW)
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ fullscreenBtn.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_FULLSCREEN)
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ splitscreenBtn.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_SPLIT_SCREEN)
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
with(context) {
// Update a11y announcement out to say "double tap to enter Fullscreen"
ViewCompat.replaceAccessibilityAction(
@@ -802,13 +869,21 @@ class HandleMenu(
private fun bindMoreActionsPill(style: MenuStyle) {
moreActionsPill.background.setTint(style.backgroundColor)
-
- arrayOf(
+ val buttons = arrayOf(
screenshotBtn to SHOULD_SHOW_SCREENSHOT_BUTTON,
newWindowBtn to shouldShowNewWindowButton,
manageWindowBtn to shouldShowManageWindowsButton,
changeAspectRatioBtn to shouldShowChangeAspectRatioButton,
- ).forEach { (button, shouldShow) ->
+ restartBtn to shouldShowRestartButton,
+ )
+ val firstVisible = buttons.find { it.second }?.first
+ val lastVisible = buttons.findLast { it.second }?.first
+
+ buttons.forEach { (button, shouldShow) ->
+ val topRadius =
+ if (button == firstVisible) handleMenuCornerRadius.toFloat() else 0f
+ val bottomRadius =
+ if (button == lastVisible) handleMenuCornerRadius.toFloat() else 0f
button.apply {
isGone = !shouldShow
textView.apply {
@@ -816,6 +891,13 @@ class HandleMenu(
startMarquee()
}
iconView.imageTintList = ColorStateList.valueOf(style.textColor)
+ background = createBackgroundDrawable(
+ color = style.textColor,
+ cornerRadius = floatArrayOf(
+ topRadius, topRadius, topRadius, topRadius,
+ bottomRadius, bottomRadius, bottomRadius, bottomRadius
+ ),
+ drawableInsets = DrawableInsets())
}
}
}
@@ -834,6 +916,10 @@ class HandleMenu(
openInAppOrBrowserBtn.apply {
contentDescription = btnText
+ background = createBackgroundDrawable(
+ color = style.textColor,
+ cornerRadius = handleMenuCornerRadius,
+ drawableInsets = DrawableInsets())
textView.apply {
text = btnText
setTextColor(style.textColor)
@@ -872,6 +958,13 @@ class HandleMenu(
fun shouldShowChangeAspectRatioButton(taskInfo: RunningTaskInfo): Boolean =
taskInfo.appCompatTaskInfo.eligibleForUserAspectRatioButton() &&
taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+
+ /**
+ * Returns whether the restart button should be shown for the task. It usually means that
+ * the task has moved to a different display.
+ */
+ fun shouldShowRestartButton(taskInfo: RunningTaskInfo): Boolean =
+ taskInfo.appCompatTaskInfo.isRestartMenuEnabledForDisplayMove
}
}
@@ -890,8 +983,10 @@ interface HandleMenuFactory {
shouldShowManageWindowsButton: Boolean,
shouldShowChangeAspectRatioButton: Boolean,
shouldShowDesktopModeButton: Boolean,
+ shouldShowRestartButton: Boolean,
isBrowserApp: Boolean,
openInAppOrBrowserIntent: Intent?,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
captionWidth: Int,
captionHeight: Int,
captionX: Int,
@@ -914,8 +1009,10 @@ object DefaultHandleMenuFactory : HandleMenuFactory {
shouldShowManageWindowsButton: Boolean,
shouldShowChangeAspectRatioButton: Boolean,
shouldShowDesktopModeButton: Boolean,
+ shouldShowRestartButton: Boolean,
isBrowserApp: Boolean,
openInAppOrBrowserIntent: Intent?,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
captionWidth: Int,
captionHeight: Int,
captionX: Int,
@@ -934,8 +1031,10 @@ object DefaultHandleMenuFactory : HandleMenuFactory {
shouldShowManageWindowsButton,
shouldShowChangeAspectRatioButton,
shouldShowDesktopModeButton,
+ shouldShowRestartButton,
isBrowserApp,
openInAppOrBrowserIntent,
+ desktopModeUiEventLogger,
captionWidth,
captionHeight,
captionX,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 5d1a7a0cc3a2..a10826fca3e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -60,14 +60,16 @@ import android.window.TaskConstants
import androidx.compose.material3.ColorScheme
import androidx.compose.ui.graphics.toArgb
import androidx.core.animation.addListener
-import androidx.core.view.ViewCompat
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_MAXIMIZE
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_RESIZE_LEFT
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_RESIZE_RIGHT
import com.android.wm.shell.desktopmode.isTaskMaximized
import com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_LINEAR_IN
@@ -85,13 +87,14 @@ import java.util.function.Supplier
* to the right or left half of the screen.
*/
class MaximizeMenu(
- private val syncQueue: SyncTransactionQueue,
- private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
- private val displayController: DisplayController,
- private val taskInfo: RunningTaskInfo,
- private val decorWindowContext: Context,
- private val positionSupplier: (Int, Int) -> Point,
- private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
+ private val syncQueue: SyncTransactionQueue,
+ private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val displayController: DisplayController,
+ private val taskInfo: RunningTaskInfo,
+ private val decorWindowContext: Context,
+ private val positionSupplier: (Int, Int) -> Point,
+ private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger
) {
private var maximizeMenu: AdditionalViewHostViewContainer? = null
private var maximizeMenuView: MaximizeMenuView? = null
@@ -132,7 +135,7 @@ class MaximizeMenu(
onLeftSnapClickListener = onLeftSnapClickListener,
onRightSnapClickListener = onRightSnapClickListener,
onHoverListener = onHoverListener,
- onOutsideTouchListener = onOutsideTouchListener
+ onOutsideTouchListener = onOutsideTouchListener,
)
maximizeMenuView?.let { view ->
view.animateOpenMenu(onEnd = {
@@ -167,7 +170,7 @@ class MaximizeMenu(
onLeftSnapClickListener: () -> Unit,
onRightSnapClickListener: () -> Unit,
onHoverListener: (Boolean) -> Unit,
- onOutsideTouchListener: () -> Unit
+ onOutsideTouchListener: () -> Unit,
) {
val t = transactionSupplier.get()
val builder = SurfaceControl.Builder()
@@ -186,6 +189,7 @@ class MaximizeMenu(
"MaximizeMenu")
maximizeMenuView = MaximizeMenuView(
context = decorWindowContext,
+ desktopModeUiEventLogger = desktopModeUiEventLogger,
sizeToggleDirection = getSizeToggleDirection(),
immersiveConfig = if (showImmersiveOption) {
MaximizeMenuView.ImmersiveConfig.Visible(
@@ -265,6 +269,7 @@ class MaximizeMenu(
*/
class MaximizeMenuView(
context: Context,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
private val sizeToggleDirection: SizeToggleDirection,
immersiveConfig: ImmersiveConfig,
showSnapOptions: Boolean,
@@ -425,7 +430,10 @@ class MaximizeMenu(
) {
super.onInitializeAccessibilityNodeInfo(host, info)
- info.addAction(AccessibilityAction.ACTION_CLICK)
+ info.addAction(AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ context.getString(R.string.maximize_menu_talkback_action_maximize_restore_text)
+ ))
host.isClickable = true
}
@@ -435,6 +443,7 @@ class MaximizeMenu(
args: Bundle?
): Boolean {
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_MAXIMIZE)
onMaximizeClickListener?.invoke()
}
return super.performAccessibilityAction(host, action, args)
@@ -447,7 +456,10 @@ class MaximizeMenu(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(host, info)
- info.addAction(AccessibilityAction.ACTION_CLICK)
+ info.addAction(AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ context.getString(R.string.maximize_menu_talkback_action_snap_left_text)
+ ))
host.isClickable = true
}
@@ -457,6 +469,7 @@ class MaximizeMenu(
args: Bundle?
): Boolean {
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_RESIZE_LEFT)
onLeftSnapClickListener?.invoke()
}
return super.performAccessibilityAction(host, action, args)
@@ -469,7 +482,10 @@ class MaximizeMenu(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(host, info)
- info.addAction(AccessibilityAction.ACTION_CLICK)
+ info.addAction(AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ context.getString(R.string.maximize_menu_talkback_action_snap_right_text)
+ ))
host.isClickable = true
}
@@ -479,35 +495,13 @@ class MaximizeMenu(
args: Bundle?
): Boolean {
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_RESIZE_RIGHT)
onRightSnapClickListener?.invoke()
}
return super.performAccessibilityAction(host, action, args)
}
}
- with(context.resources) {
- ViewCompat.replaceAccessibilityAction(
- snapLeftButton,
- AccessibilityActionCompat.ACTION_CLICK,
- getString(R.string.maximize_menu_talkback_action_snap_left_text),
- null
- )
-
- ViewCompat.replaceAccessibilityAction(
- snapRightButton,
- AccessibilityActionCompat.ACTION_CLICK,
- getString(R.string.maximize_menu_talkback_action_snap_right_text),
- null
- )
-
- ViewCompat.replaceAccessibilityAction(
- sizeToggleButton,
- AccessibilityActionCompat.ACTION_CLICK,
- getString(R.string.maximize_menu_talkback_action_maximize_restore_text),
- null
- )
- }
-
// Maximize/restore button.
val sizeToggleBtnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
R.string.desktop_mode_maximize_menu_restore_button_text
@@ -792,15 +786,15 @@ class MaximizeMenu(
}
/** Measure width of the root view of this menu. */
- fun measureWidth() : Int {
- rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- return rootView.getMeasuredWidth()
+ fun measureWidth(): Int {
+ rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
+ return rootView.measuredWidth
}
/** Measure height of the root view of this menu. */
- fun measureHeight() : Int {
- rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- return rootView.getMeasuredHeight()
+ fun measureHeight(): Int {
+ rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
+ return rootView.measuredHeight
}
private fun deactivateSnapOptions() {
@@ -1048,7 +1042,8 @@ interface MaximizeMenuFactory {
taskInfo: RunningTaskInfo,
decorWindowContext: Context,
positionSupplier: (Int, Int) -> Point,
- transactionSupplier: Supplier<Transaction>
+ transactionSupplier: Supplier<Transaction>,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
): MaximizeMenu
}
@@ -1061,7 +1056,8 @@ object DefaultMaximizeMenuFactory : MaximizeMenuFactory {
taskInfo: RunningTaskInfo,
decorWindowContext: Context,
positionSupplier: (Int, Int) -> Point,
- transactionSupplier: Supplier<Transaction>
+ transactionSupplier: Supplier<Transaction>,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
): MaximizeMenu {
return MaximizeMenu(
syncQueue,
@@ -1070,7 +1066,8 @@ object DefaultMaximizeMenuFactory : MaximizeMenuFactory {
taskInfo,
decorWindowContext,
positionSupplier,
- transactionSupplier
+ transactionSupplier,
+ desktopModeUiEventLogger
)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 5e4a0a5860f0..1563259f4a1a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -18,7 +18,6 @@ package com.android.wm.shell.windowdecor;
import android.app.ActivityManager;
import android.view.SurfaceControl;
-import android.window.TransitionInfo;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -84,14 +83,12 @@ public interface WindowDecorViewModel {
* @param taskSurface the surface of the task
* @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition
- * @param changeMode the type of change to the task
*/
void onTaskChanging(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT,
- @TransitionInfo.TransitionMode int changeMode);
+ SurfaceControl.Transaction finishT);
/**
* Notifies that the given task is about to close to give the window decoration a chance to
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt
index 39ccf5bd03a7..950eeccf6a4a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/AppHandleAndHeaderVisibilityHelper.kt
@@ -23,6 +23,7 @@ import android.view.WindowManager
import android.window.DesktopExperienceFlags.ENABLE_BUG_FIXES_FOR_SECONDARY_DISPLAY
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.DesktopWallpaperActivity.Companion.isWallpaperTask
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -52,7 +53,8 @@ class AppHandleAndHeaderVisibilityHelper (
private fun allowedForTask(taskInfo: ActivityManager.RunningTaskInfo): Boolean {
// TODO (b/382023296): Remove once we no longer rely on
// Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay
- if (displayController.getDisplay(taskInfo.displayId) == null) {
+ val display = displayController.getDisplay(taskInfo.displayId)
+ if (display == null) {
// If DisplayController doesn't have it tracked, it could be a private/managed display.
return false
}
@@ -68,8 +70,7 @@ class AppHandleAndHeaderVisibilityHelper (
// TODO (b/382023296): Remove once we no longer rely on
// Flags.enableBugFixesForSecondaryDisplay as it is taken care of in #allowedForDisplay
val isOnLargeScreen =
- displayController.getDisplay(taskInfo.displayId).minSizeDimensionDp >=
- WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
+ display.minSizeDimensionDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
if (!DesktopModeStatus.canEnterDesktopMode(context)
&& DesktopModeStatus.overridesShowAppHandle(context)
&& !isOnLargeScreen
@@ -78,6 +79,14 @@ class AppHandleAndHeaderVisibilityHelper (
// small screens
return false
}
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+ && !DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, display)
+ ) {
+ // TODO(b/388853233): enable handles for split tasks once drag to bubble is enabled
+ if (taskInfo.windowingMode != WindowConfiguration.WINDOWING_MODE_FULLSCREEN) {
+ return false
+ }
+ }
return DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)
&& !isWallpaperTask(taskInfo)
&& taskInfo.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ButtonBackgroundDrawableUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ButtonBackgroundDrawableUtils.kt
index f08cfa987cc7..33e743016d0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ButtonBackgroundDrawableUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ButtonBackgroundDrawableUtils.kt
@@ -51,10 +51,20 @@ fun replaceColorAlpha(@ColorInt color: Int, alpha: Int): Int {
*/
fun createBackgroundDrawable(
@ColorInt color: Int, cornerRadius: Int, drawableInsets: DrawableInsets
+): Drawable = createBackgroundDrawable(
+ color,
+ FloatArray(8) { cornerRadius.toFloat() },
+ drawableInsets)
+
+/**
+ * Creates a background drawable with specified color, corner radius, and insets.
+ */
+fun createBackgroundDrawable(
+ @ColorInt color: Int, cornerRadius: FloatArray, drawableInsets: DrawableInsets
): Drawable = LayerDrawable(arrayOf(
ShapeDrawable().apply {
shape = RoundRectShape(
- FloatArray(8) { cornerRadius.toFloat() },
+ cornerRadius,
/* inset= */ null,
/* innerRadii= */ null
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
index c5057aa3cc18..f09f22fb1951 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
@@ -92,4 +92,7 @@ internal class DecorThemeUtil(private val context: Context) {
Theme.LIGHT -> lightColors
Theme.DARK -> darkColors
}
+
+ fun getColorScheme(isDarkMode: Boolean): ColorScheme =
+ if (isDarkMode) darkColors else lightColors
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
index 57f8046065b4..ab6b72947586 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
@@ -198,6 +198,11 @@ class DesktopTilingDividerWindowManager(
tilingDividerView?.onUiModeChange(isDarkMode)
}
+ /** Notifies the divider view of task info change and possible color change. */
+ fun onTaskInfoChange() {
+ tilingDividerView?.onTaskInfoChange()
+ }
+
/** Hides the divider bar. */
fun hideDividerBar() {
if (!dividerShown) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 854d9e1deef1..3e92d7d994a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -375,6 +375,7 @@ class DesktopTilingWindowDecoration(
fun onTaskInfoChange(taskInfo: RunningTaskInfo) {
val isCurrentTaskInDarkMode = isTaskInDarkMode(taskInfo)
+ desktopTilingDividerWindowManager?.onTaskInfoChange()
if (isCurrentTaskInDarkMode == isDarkMode || !isTilingManagerInitialised) return
isDarkMode = isCurrentTaskInDarkMode
desktopTilingDividerWindowManager?.onUiModeChange(isDarkMode)
@@ -432,15 +433,38 @@ class DesktopTilingWindowDecoration(
startTransaction: Transaction,
finishTransaction: Transaction,
) {
+ var leftTaskBroughtToFront = false
+ var rightTaskBroughtToFront = false
+
for (change in info.changes) {
change.taskInfo?.let {
if (it.isFullscreen || isMinimized(change.mode, info.type)) {
removeTaskIfTiled(it.taskId, /* taskVanished= */ false, it.isFullscreen)
} else if (isEnteringPip(change, info.type)) {
removeTaskIfTiled(it.taskId, /* taskVanished= */ true, it.isFullscreen)
+ } else if (isTransitionToFront(change.mode, info.type)) {
+ handleTaskBroughtToFront(it.taskId)
+ leftTaskBroughtToFront =
+ leftTaskBroughtToFront ||
+ it.taskId == leftTaskResizingHelper?.taskInfo?.taskId
+ rightTaskBroughtToFront =
+ rightTaskBroughtToFront ||
+ it.taskId == rightTaskResizingHelper?.taskInfo?.taskId
}
}
}
+
+ if (leftTaskBroughtToFront && rightTaskBroughtToFront) {
+ desktopTilingDividerWindowManager?.showDividerBar()
+ }
+ }
+
+ private fun handleTaskBroughtToFront(taskId: Int) {
+ if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
+ leftTaskResizingHelper?.onAppBecomingVisible()
+ } else if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
+ rightTaskResizingHelper?.onAppBecomingVisible()
+ }
}
private fun isMinimized(changeMode: Int, infoType: Int): Boolean {
@@ -471,6 +495,9 @@ class DesktopTilingWindowDecoration(
return false
}
+ private fun isTransitionToFront(changeMode: Int, transitionType: Int): Boolean =
+ changeMode == TRANSIT_TO_FRONT && transitionType == TRANSIT_TO_FRONT
+
class AppResizingHelper(
val taskInfo: RunningTaskInfo,
val desktopModeWindowDecoration: DesktopModeWindowDecoration,
@@ -484,6 +511,7 @@ class DesktopTilingWindowDecoration(
) {
var isInitialised = false
var newBounds = Rect(bounds)
+ var visibilityCallback: (() -> Unit)? = null
private lateinit var resizeVeil: ResizeVeil
private val displayContext = displayController.getDisplayContext(taskInfo.displayId)
private val userContext =
@@ -521,6 +549,11 @@ class DesktopTilingWindowDecoration(
fun updateVeil(t: Transaction) = resizeVeil.updateTransactionWithResizeVeil(t, newBounds)
+ fun onAppBecomingVisible() {
+ visibilityCallback?.invoke()
+ visibilityCallback = null
+ }
+
fun hideVeil() = resizeVeil.hideVeil()
private fun createIconFactory(context: Context, dimensions: Int): BaseIconFactory {
@@ -593,11 +626,16 @@ class DesktopTilingWindowDecoration(
removeTask(leftTaskResizingHelper, taskVanished, shouldDelayUpdate)
leftTaskResizingHelper = null
val taskId = rightTaskResizingHelper?.taskInfo?.taskId
- if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ val callback: (() -> Unit)? = {
rightTaskResizingHelper
?.desktopModeWindowDecoration
?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
}
+ if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ callback?.invoke()
+ } else if (rightTaskResizingHelper != null) {
+ rightTaskResizingHelper?.visibilityCallback = callback
+ }
tearDownTiling()
return
}
@@ -607,11 +645,17 @@ class DesktopTilingWindowDecoration(
removeTask(rightTaskResizingHelper, taskVanished, shouldDelayUpdate)
rightTaskResizingHelper = null
val taskId = leftTaskResizingHelper?.taskInfo?.taskId
- if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ val callback: (() -> Unit)? = {
leftTaskResizingHelper
?.desktopModeWindowDecoration
?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
}
+ if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ callback?.invoke()
+ } else if (leftTaskResizingHelper != null) {
+ leftTaskResizingHelper?.visibilityCallback = callback
+ }
+
tearDownTiling()
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
index 54dcd2d082dc..e9bd6f8ef85c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
@@ -29,6 +29,7 @@ import android.view.RoundedCorner
import android.view.View
import android.view.ViewConfiguration
import android.widget.FrameLayout
+import androidx.compose.ui.graphics.toArgb
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
import com.android.wm.shell.R
@@ -36,6 +37,7 @@ import com.android.wm.shell.common.split.DividerHandleView
import com.android.wm.shell.common.split.DividerRoundedCorner
import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.windowdecor.DragDetector
+import com.android.wm.shell.windowdecor.common.DecorThemeUtil
/** Divider for tiling split screen, currently mostly a copy of [DividerView]. */
class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.MotionEventHandler {
@@ -56,6 +58,9 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
@VisibleForTesting var handleY: IntRange = 0..0
private var canResize = false
private var resized = false
+ private var isDarkMode = false
+ private var decorThemeUtil = DecorThemeUtil(context)
+
/**
* Tracks divider bar visible bounds in screen-based coordination. Used to calculate with
* insets.
@@ -90,10 +95,12 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
) {
callback = dividerMoveCallback
this.dividerBounds.set(dividerBounds)
+ this.isDarkMode = isDarkMode
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
handle.setIsLeftRightSplit(true)
handle.setup(/* isSplitScreen= */ false, isDarkMode)
corners.setIsLeftRightSplit(true)
- corners.setup(/* isSplitScreen= */ false, isDarkMode)
+ corners.setup(/* isSplitScreen= */ false, paint.color)
handleRegionHeight = handleRegionSize.height
handleRegionWidth = handleRegionSize.width
cornersRadius =
@@ -108,17 +115,22 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
}
fun onUiModeChange(isDarkMode: Boolean) {
+ this.isDarkMode = isDarkMode
handle.onUiModeChange(isDarkMode)
- corners.onUiModeChange(isDarkMode)
- paint.color =
- if (isDarkMode) {
- resources.getColor(R.color.tiling_divider_background_dark, null /* theme */)
- } else {
- resources.getColor(R.color.tiling_divider_background_light, null /* theme */)
- }
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
+ corners.onUiModeChange(paint.color)
invalidate()
}
+ fun onTaskInfoChange() {
+ decorThemeUtil = DecorThemeUtil(context)
+ if (paint.color != decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()) {
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
+ corners.onCornerColorChange(paint.color)
+ invalidate()
+ }
+ }
+
override fun onFinishInflate() {
super.onFinishInflate()
dividerBar = requireViewById(R.id.divider_bar)
@@ -128,15 +140,10 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
resources.getDimensionPixelSize(R.dimen.docked_stack_divider_lift_elevation)
setOnTouchListener(this)
setWillNotDraw(false)
- paint.color =
- if (
- context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
- Configuration.UI_MODE_NIGHT_YES
- ) {
- resources.getColor(R.color.tiling_divider_background_dark, /* theme= */null)
- } else {
- resources.getColor(R.color.tiling_divider_background_light, /* theme= */ null)
- }
+ val isDarkMode =
+ context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
+ Configuration.UI_MODE_NIGHT_YES
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
paint.isAntiAlias = true
paint.style = Paint.Style.FILL
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 9d16be59ba34..f09c500dcdd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -39,6 +39,8 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit
import com.android.internal.policy.SystemBarUtils
import com.android.window.flags.Flags
import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_OPENED
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.windowdecor.AppHandleAnimator
import com.android.wm.shell.windowdecor.WindowManagerWrapper
@@ -53,7 +55,8 @@ class AppHandleViewHolder(
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: OnClickListener,
private val windowManagerWrapper: WindowManagerWrapper,
- private val handler: Handler
+ private val handler: Handler,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
) : WindowDecorationViewHolder<AppHandleViewHolder.HandleData>(rootView) {
data class HandleData(
@@ -201,6 +204,7 @@ class AppHandleViewHolder(
// Passthrough the a11y click action so the caption handle, so that app handle menu
// is opened on a11y click, similar to a real click
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_OPENED)
captionHandle.performClick()
}
return super.performAccessibilityAction(host, action, args)
@@ -216,10 +220,13 @@ class AppHandleViewHolder(
}
}
- // Update a11y action text so that Talkback announces "Press double tap to open app handle
- // menu" while focused on status bar input layer
+ // Update a11y action text so that Talkback announces "Press double tap to open menu"
+ // while focused on status bar input layer
ViewCompat.replaceAccessibilityAction(
- view, AccessibilityActionCompat.ACTION_CLICK, "Open app handle menu", null
+ view,
+ AccessibilityActionCompat.ACTION_CLICK,
+ context.getString(R.string.app_handle_chip_accessibility_announce),
+ null
)
}
@@ -297,12 +304,14 @@ class AppHandleViewHolder(
onCaptionButtonClickListener: OnClickListener,
windowManagerWrapper: WindowManagerWrapper,
handler: Handler,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
): AppHandleViewHolder = AppHandleViewHolder(
rootView,
onCaptionTouchListener,
onCaptionButtonClickListener,
windowManagerWrapper,
handler,
+ desktopModeUiEventLogger,
)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index 0e2698d0b6fa..d2e3a07f4651 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -49,6 +49,13 @@ import com.android.internal.R.color.materialColorSurfaceContainerHigh
import com.android.internal.R.color.materialColorSurfaceContainerLow
import com.android.internal.R.color.materialColorSurfaceDim
import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_MAXIMIZE_RESTORE
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_RESIZE_LEFT
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_RESIZE_RIGHT
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_CLOSE_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_MINIMIZE_BUTTON
import com.android.wm.shell.windowdecor.MaximizeButtonView
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.common.DrawableInsets
@@ -75,6 +82,7 @@ class AppHeaderViewHolder(
mOnRightSnapClickListener: () -> Unit,
mOnMaximizeOrRestoreClickListener: () -> Unit,
onMaximizeHoverAnimationFinishedListener: () -> Unit,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
) : WindowDecorationViewHolder<AppHeaderViewHolder.HeaderData>(rootView) {
data class HeaderData(
@@ -151,6 +159,8 @@ class AppHeaderViewHolder(
private lateinit var a11yTextMaximize: String
private lateinit var a11yTextRestore: String
+ private lateinit var currentTaskInfo: RunningTaskInfo
+
init {
captionView.setOnTouchListener(onCaptionTouchListener)
captionHandle.setOnTouchListener(onCaptionTouchListener)
@@ -197,9 +207,18 @@ class AppHeaderViewHolder(
args: Bundle?
): Boolean {
when (action) {
- R.id.action_snap_left -> mOnLeftSnapClickListener.invoke()
- R.id.action_snap_right -> mOnRightSnapClickListener.invoke()
- R.id.action_maximize_restore -> mOnMaximizeOrRestoreClickListener.invoke()
+ R.id.action_snap_left -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_LEFT)
+ mOnLeftSnapClickListener.invoke()
+ }
+ R.id.action_snap_right -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_RIGHT)
+ mOnRightSnapClickListener.invoke()
+ }
+ R.id.action_maximize_restore -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_MAXIMIZE_RESTORE)
+ mOnMaximizeOrRestoreClickListener.invoke()
+ }
}
return super.performAccessibilityAction(host, action, args)
@@ -224,10 +243,56 @@ class AppHeaderViewHolder(
args: Bundle?
): Boolean {
when (action) {
- AccessibilityAction.ACTION_CLICK.id -> host.performClick()
- R.id.action_snap_left -> mOnLeftSnapClickListener.invoke()
- R.id.action_snap_right -> mOnRightSnapClickListener.invoke()
- R.id.action_maximize_restore -> mOnMaximizeOrRestoreClickListener.invoke()
+ AccessibilityAction.ACTION_CLICK.id -> {
+ desktopModeUiEventLogger.log(
+ currentTaskInfo, A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON
+ )
+ host.performClick()
+ }
+ R.id.action_snap_left -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_LEFT)
+ mOnLeftSnapClickListener.invoke()
+ }
+ R.id.action_snap_right -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_RIGHT)
+ mOnRightSnapClickListener.invoke()
+ }
+ R.id.action_maximize_restore -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_MAXIMIZE_RESTORE)
+ mOnMaximizeOrRestoreClickListener.invoke()
+ }
+ }
+
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ closeWindowButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ when (action) {
+ AccessibilityAction.ACTION_CLICK.id -> desktopModeUiEventLogger.log(
+ currentTaskInfo, A11Y_APP_WINDOW_CLOSE_BUTTON
+ )
+ }
+
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ minimizeWindowButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ when (action) {
+ AccessibilityAction.ACTION_CLICK.id -> desktopModeUiEventLogger.log(
+ currentTaskInfo, A11Y_APP_WINDOW_MINIMIZE_BUTTON
+ )
}
return super.performAccessibilityAction(host, action, args)
@@ -310,7 +375,8 @@ class AppHeaderViewHolder(
enableMaximizeLongClick: Boolean,
isCaptionVisible: Boolean,
) {
- if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) {
+ currentTaskInfo = taskInfo
+ if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue) {
bindDataWithThemedHeaders(
taskInfo,
isTaskMaximized,
@@ -361,7 +427,7 @@ class AppHeaderViewHolder(
minimizeWindowButton.background = getDrawable(1)
}
maximizeButtonView.setAnimationTints(isDarkMode())
- minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()
+ minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue
}
private fun bindDataWithThemedHeaders(
@@ -417,7 +483,7 @@ class AppHeaderViewHolder(
drawableInsets = minimizeDrawableInsets
)
}
- minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()
+ minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue
// Maximize button.
maximizeButtonView.apply {
setAnimationTints(
@@ -761,6 +827,7 @@ class AppHeaderViewHolder(
mOnRightSnapClickListener: () -> Unit,
mOnMaximizeOrRestoreClickListener: () -> Unit,
onMaximizeHoverAnimationFinishedListener: () -> Unit,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger
): AppHeaderViewHolder = AppHeaderViewHolder(
rootView,
onCaptionTouchListener,
@@ -771,6 +838,7 @@ class AppHeaderViewHolder(
mOnRightSnapClickListener,
mOnMaximizeOrRestoreClickListener,
onMaximizeHoverAnimationFinishedListener,
+ desktopModeUiEventLogger,
)
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index fa9864b539ee..7cd2bcc8efd4 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -52,7 +52,7 @@ abstract class SwitchAppByDoubleTapDividerBenchmark(override val flicker: Legacy
)
}
transitions {
- SplitScreenUtils.doubleTapDividerToSwitch(device)
+ SplitScreenUtils.doubleTapDividerToSwitch(device, instrumentation.uiAutomation)
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
waitForLayersToSwitch(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt
index 3fd93d3eaf59..dfc737174a4f 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -61,7 +61,7 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0) {
@Test
open fun switchAppByDoubleTapDivider() {
- SplitScreenUtils.doubleTapDividerToSwitch(device)
+ SplitScreenUtils.doubleTapDividerToSwitch(device, instrumentation.uiAutomation)
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
waitForLayersToSwitch(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
index 68f7ef09ee70..f9b69d3f5f7e 100644
--- a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/SimulatedConnectedDisplayTestRule.kt
@@ -41,7 +41,6 @@ import org.junit.runners.model.Statement
class SimulatedConnectedDisplayTestRule : TestRule {
private val context = InstrumentationRegistry.getInstrumentation().targetContext
- private val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
private val displayManager = context.getSystemService(DisplayManager::class.java)
private val addedDisplays = mutableListOf<Int>()
@@ -102,7 +101,8 @@ class SimulatedConnectedDisplayTestRule : TestRule {
// Add the overlay displays
Settings.Global.putString(
InstrumentationRegistry.getInstrumentation().context.contentResolver,
- Settings.Global.OVERLAY_DISPLAY_DEVICES, displaySettings
+ Settings.Global.OVERLAY_DISPLAY_DEVICES,
+ displaySettings
)
withTimeoutOrNull(TIMEOUT) {
displayAddedFlow.take(displays.size).collect { displayId ->
@@ -125,10 +125,6 @@ class SimulatedConnectedDisplayTestRule : TestRule {
}
private fun cleanupTestDisplays() = runBlocking {
- if (addedDisplays.isEmpty()) {
- return@runBlocking
- }
-
val displayRemovedFlow: Flow<Int> = callbackFlow {
val listener = object : DisplayListener {
override fun onDisplayAdded(displayId: Int) {}
@@ -146,16 +142,24 @@ class SimulatedConnectedDisplayTestRule : TestRule {
}
}
- // Remove overlay displays
+ // Remove overlay displays. We'll execute this regardless of addedDisplays just to
+ // ensure all overlay displays are removed before and after the test.
+ // Note: If we want to restore the original overlay display added before this test (and its
+ // topology), it will be complicated as re-adding overlay display would lead to different
+ // displayId and topology could not be restored easily.
Settings.Global.putString(
InstrumentationRegistry.getInstrumentation().context.contentResolver,
- Settings.Global.OVERLAY_DISPLAY_DEVICES, null)
+ Settings.Global.OVERLAY_DISPLAY_DEVICES,
+ null
+ )
- withTimeoutOrNull(TIMEOUT) {
- displayRemovedFlow.take(addedDisplays.size).collect { displayId ->
- addedDisplays.remove(displayId)
- }
- } ?: error("Timed out waiting for displays to be removed.")
+ if (!addedDisplays.isEmpty()) {
+ withTimeoutOrNull(TIMEOUT) {
+ displayRemovedFlow.take(addedDisplays.size).collect { displayId ->
+ addedDisplays.remove(displayId)
+ }
+ } ?: error("Timed out waiting for displays to be removed: $addedDisplays")
+ }
}
private companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 7b6cfe3f9f8a..98b0bd0b589d 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -38,15 +38,15 @@ java_library {
],
static_libs: [
"androidx.test.ext.junit",
- "flickertestapplib",
+ "com_android_wm_shell_flags_lib",
"flickerlib",
"flickerlib-helpers",
+ "flickertestapplib",
+ "launcher-aosp-tapl",
+ "launcher-helper-lib",
"platform-test-annotations",
"wm-flicker-common-app-helpers",
"wm-flicker-common-assertions",
- "launcher-helper-lib",
- "launcher-aosp-tapl",
- "com_android_wm_shell_flags_lib",
],
}
@@ -60,18 +60,18 @@ java_defaults {
test_suites: ["device-tests"],
libs: ["android.test.runner.stubs.system"],
static_libs: [
- "wm-shell-flicker-utils",
"androidx.test.ext.junit",
- "flickertestapplib",
"flickerlib",
"flickerlib-helpers",
"flickerlib-trace_processor_shell",
+ "flickertestapplib",
+ "launcher-aosp-tapl",
+ "launcher-helper-lib",
"platform-test-annotations",
"platform-test-rules",
"wm-flicker-common-app-helpers",
"wm-flicker-common-assertions",
- "launcher-helper-lib",
- "launcher-aosp-tapl",
+ "wm-shell-flicker-utils",
],
data: [
":FlickerTestApp",
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index e4183f16ba14..e54930d730f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.utils
import android.app.Instrumentation
+import android.app.UiAutomation
import android.content.Context
import android.graphics.Point
import android.os.SystemClock
@@ -355,13 +356,40 @@ object SplitScreenUtils {
)
}
- fun doubleTapDividerToSwitch(device: UiDevice) {
+ fun doubleTapDividerToSwitch(device: UiDevice, uiAutomation: UiAutomation) {
val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
- val interval =
- (ViewConfiguration.getDoubleTapTimeout() + ViewConfiguration.getDoubleTapMinTime()) / 2
- dividerBar.click()
- SystemClock.sleep(interval.toLong())
- dividerBar.click()
+ val x = dividerBar.visibleCenter.x.toFloat()
+ val y = dividerBar.visibleCenter.y.toFloat()
+
+ // To send a double-tap action, we set a DOWN event, then UP, then DOWN, then, UP.
+ val startTime = SystemClock.uptimeMillis()
+ val timeOfFirstUp = startTime + ViewConfiguration.getTapTimeout()
+ // Between the two taps, we wait an arbitrary amount of time between the min and max times
+ // for a double-tap.
+ val timeOfSecondDown = timeOfFirstUp + ViewConfiguration.getDoubleTapMinTime() +
+ ((ViewConfiguration.getDoubleTapTimeout() -
+ ViewConfiguration.getDoubleTapMinTime()) / 4)
+ val timeOfSecondUp = timeOfSecondDown + ViewConfiguration.getTapTimeout()
+
+ val downEvent = MotionEvent.obtain(startTime, startTime, MotionEvent.ACTION_DOWN, x, y,
+ 0 /* metaState */)
+ downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN)
+ uiAutomation.injectInputEvent(downEvent, true)
+
+ val upEvent = MotionEvent.obtain(startTime, timeOfFirstUp, MotionEvent.ACTION_UP, x, y,
+ 0 /* metaState */)
+ upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN)
+ uiAutomation.injectInputEvent(upEvent, true)
+
+ val downEvent2 = MotionEvent.obtain(timeOfSecondDown, timeOfSecondDown,
+ MotionEvent.ACTION_DOWN, x, y, 0 /* metaState */)
+ downEvent2.setSource(InputDevice.SOURCE_TOUCHSCREEN)
+ uiAutomation.injectInputEvent(downEvent2, true)
+
+ val upEvent2 = MotionEvent.obtain(timeOfSecondDown, timeOfSecondUp, MotionEvent.ACTION_UP,
+ x, y, 0 /* metaState */)
+ upEvent2.setSource(InputDevice.SOURCE_TOUCHSCREEN)
+ uiAutomation.injectInputEvent(upEvent2, true)
}
fun copyContentInSplit(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
index 3d5e9495e29d..1e459d55ed77 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.common;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -231,4 +232,24 @@ public class DisplayControllerTests extends ShellTestCase {
assertEquals(DISPLAY_ABS_BOUNDS_1,
mController.getDisplayLayout(DISPLAY_ID_1).globalBoundsDp());
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
+ public void onDisplayConfigurationChanged_reInitDisplayLayout()
+ throws RemoteException {
+ ExtendedMockito.doReturn(true)
+ .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mController.onInit();
+ mController.addDisplayWindowListener(mListener);
+
+ mCapturedTopologyListener.accept(mMockTopology);
+
+ DisplayLayout displayLayoutBefore = mController.getDisplayLayout(DISPLAY_ID_0);
+ mDisplayContainerListener.onDisplayConfigurationChanged(DISPLAY_ID_0, new Configuration());
+ DisplayLayout displayLayoutAfter = mController.getDisplayLayout(DISPLAY_ID_0);
+
+ assertNotSame(displayLayoutBefore, displayLayoutAfter);
+ assertEquals(DISPLAY_ABS_BOUNDS_0,
+ mController.getDisplayLayout(DISPLAY_ID_0).globalBoundsDp());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index 9268db60aa51..42dcaf4b4f33 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -23,6 +23,7 @@ import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.server.display.feature.flags.Flags as DisplayFlags
import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
@@ -50,6 +51,7 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@@ -212,12 +214,15 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun testUserChanged_createsDeskWhenNeeded() =
testScope.runTest {
+ val userId = 11
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
val userChangeListenerCaptor = argumentCaptor<UserChangeListener>()
verify(mockShellController).addUserChangeListener(userChangeListenerCaptor.capture())
- whenever(mockDesktopRepository.getNumberOfDesks(displayId = 2)).thenReturn(0)
- whenever(mockDesktopRepository.getNumberOfDesks(displayId = 3)).thenReturn(0)
- whenever(mockDesktopRepository.getNumberOfDesks(displayId = 4)).thenReturn(1)
+ val mockRepository = mock<DesktopRepository>()
+ whenever(mockDesktopUserRepositories.getProfile(userId)).thenReturn(mockRepository)
+ whenever(mockRepository.getNumberOfDesks(displayId = 2)).thenReturn(0)
+ whenever(mockRepository.getNumberOfDesks(displayId = 3)).thenReturn(0)
+ whenever(mockRepository.getNumberOfDesks(displayId = 4)).thenReturn(1)
whenever(mockRootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(2, 3, 4))
desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
handler.onDisplayAdded(displayId = 2)
@@ -226,7 +231,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
runCurrent()
clearInvocations(mockDesktopTasksController)
- userChangeListenerCaptor.lastValue.onUserChanged(1, context)
+ userChangeListenerCaptor.lastValue.onUserChanged(userId, context)
runCurrent()
verify(mockDesktopTasksController).createDesk(displayId = 2)
@@ -237,13 +242,22 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Test
fun testConnectExternalDisplay() {
onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
- verify(desktopDisplayModeController).refreshDisplayWindowingMode()
+ verify(desktopDisplayModeController).updateExternalDisplayWindowingMode(externalDisplayId)
+ verify(desktopDisplayModeController).updateDefaultDisplayWindowingMode()
}
@Test
fun testDisconnectExternalDisplay() {
onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
- verify(desktopDisplayModeController).refreshDisplayWindowingMode()
+ verify(desktopDisplayModeController).updateDefaultDisplayWindowingMode()
+ }
+
+ @Test
+ @EnableFlags(DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ fun testDesktopModeEligibleChanged() {
+ onDisplaysChangedListenerCaptor.lastValue.onDesktopModeEligibleChanged(externalDisplayId)
+ verify(desktopDisplayModeController).updateExternalDisplayWindowingMode(externalDisplayId)
+ verify(desktopDisplayModeController).updateDefaultDisplayWindowingMode()
}
private class FakeDesktopRepositoryInitializer : DesktopRepositoryInitializer {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index 96b826f93aae..7e9ee34c8f68 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -101,10 +101,13 @@ class DesktopDisplayModeControllerTest(
private val fullscreenTask =
TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ private val externalTDA = DisplayAreaInfo(MockToken().token(), EXTERNAL_DISPLAY_ID, 0)
private val wallpaperToken = MockToken().token()
private val defaultDisplay = mock<Display>()
private val externalDisplay = mock<Display>()
- private val mouseDevice = mock<InputDevice>()
+ private val touchpadDevice = mock<InputDevice>()
+ private val keyboardDevice = mock<InputDevice>()
+ private val connectedDeviceIds = mutableListOf<Int>()
private lateinit var extendedDisplaySettingsRestoreSession:
ExtendedDisplaySettingsRestoreSession
@@ -127,6 +130,8 @@ class DesktopDisplayModeControllerTest(
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder())
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
.thenReturn(defaultTDA)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(EXTERNAL_DISPLAY_ID))
+ .thenReturn(externalTDA)
controller =
DesktopDisplayModeController(
context,
@@ -145,16 +150,18 @@ class DesktopDisplayModeControllerTest(
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
whenever(displayController.getDisplay(DEFAULT_DISPLAY)).thenReturn(defaultDisplay)
whenever(displayController.getDisplay(EXTERNAL_DISPLAY_ID)).thenReturn(externalDisplay)
- setTabletModeStatus(SwitchState.UNKNOWN)
- whenever(
- DesktopModeStatus.isDesktopModeSupportedOnDisplay(
- context,
- defaultDisplay
- )
- ).thenReturn(true)
- whenever(mouseDevice.supportsSource(InputDevice.SOURCE_MOUSE)).thenReturn(true)
- whenever(inputManager.getInputDevice(EXTERNAL_DEVICE_ID)).thenReturn(mouseDevice)
- setMouseConnected(false)
+ whenever(DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, defaultDisplay))
+ .thenReturn(true)
+ whenever(touchpadDevice.supportsSource(InputDevice.SOURCE_TOUCHPAD)).thenReturn(true)
+ whenever(touchpadDevice.isEnabled()).thenReturn(true)
+ whenever(inputManager.getInputDevice(TOUCHPAD_DEVICE_ID)).thenReturn(touchpadDevice)
+ whenever(keyboardDevice.isFullKeyboard()).thenReturn(true)
+ whenever(keyboardDevice.isVirtual()).thenReturn(false)
+ whenever(keyboardDevice.isEnabled()).thenReturn(true)
+ whenever(inputManager.getInputDevice(KEYBOARD_DEVICE_ID)).thenReturn(keyboardDevice)
+ whenever(inputManager.inputDeviceIds).thenAnswer { connectedDeviceIds.toIntArray() }
+ setTouchpadConnected(false)
+ setKeyboardConnected(false)
}
@After
@@ -211,8 +218,8 @@ class DesktopDisplayModeControllerTest(
@DisableFlags(Flags.FLAG_FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH)
fun testTargetWindowingMode_formfactorDisabled(
@TestParameter param: ExternalDisplayBasedTargetModeTestCase,
- @TestParameter tabletModeStatus: SwitchState,
- @TestParameter hasAnyMouseDevice: Boolean,
+ @TestParameter hasAnyTouchpadDevice: Boolean,
+ @TestParameter hasAnyKeyboardDevice: Boolean,
) {
whenever(mockWindowManager.getWindowingMode(anyInt()))
.thenReturn(param.defaultWindowingMode)
@@ -221,15 +228,11 @@ class DesktopDisplayModeControllerTest(
} else {
disconnectExternalDisplay()
}
- setTabletModeStatus(tabletModeStatus)
- setMouseConnected(hasAnyMouseDevice)
+ setTouchpadConnected(hasAnyTouchpadDevice)
+ setKeyboardConnected(hasAnyKeyboardDevice)
setExtendedMode(param.extendedDisplayEnabled)
- whenever(
- DesktopModeStatus.isDesktopModeSupportedOnDisplay(
- context,
- defaultDisplay
- )
- ).thenReturn(param.isDefaultDisplayDesktopEligible)
+ whenever(DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, defaultDisplay))
+ .thenReturn(param.isDefaultDisplayDesktopEligible)
assertThat(controller.getTargetWindowingModeForDefaultDisplay())
.isEqualTo(param.expectedWindowingMode)
@@ -246,15 +249,11 @@ class DesktopDisplayModeControllerTest(
} else {
disconnectExternalDisplay()
}
- setTabletModeStatus(param.tabletModeStatus)
setExtendedMode(param.extendedDisplayEnabled)
- whenever(
- DesktopModeStatus.isDesktopModeSupportedOnDisplay(
- context,
- defaultDisplay
- )
- ).thenReturn(param.isDefaultDisplayDesktopEligible)
- setMouseConnected(param.hasAnyMouseDevice)
+ whenever(DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, defaultDisplay))
+ .thenReturn(param.isDefaultDisplayDesktopEligible)
+ setTouchpadConnected(param.hasAnyTouchpadDevice)
+ setKeyboardConnected(param.hasAnyKeyboardDevice)
assertThat(controller.getTargetWindowingModeForDefaultDisplay())
.isEqualTo(param.expectedWindowingMode)
@@ -296,30 +295,36 @@ class DesktopDisplayModeControllerTest(
.isEqualTo(WINDOWING_MODE_UNDEFINED)
}
+ @Test
+ @EnableFlags(DisplayFlags.FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ fun externalDisplayWindowingMode() {
+ externalTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ setExtendedMode(true)
+
+ controller.updateExternalDisplayWindowingMode(EXTERNAL_DISPLAY_ID)
+
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1)).startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[externalTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
private fun connectExternalDisplay() {
whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
.thenReturn(intArrayOf(DEFAULT_DISPLAY, EXTERNAL_DISPLAY_ID))
- controller.refreshDisplayWindowingMode()
+ controller.updateDefaultDisplayWindowingMode()
}
private fun disconnectExternalDisplay() {
whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
.thenReturn(intArrayOf(DEFAULT_DISPLAY))
- controller.refreshDisplayWindowingMode()
- }
-
- private fun setTabletModeStatus(status: SwitchState) {
- whenever(inputManager.isInTabletMode()).thenReturn(status.value)
+ controller.updateDefaultDisplayWindowingMode()
}
private fun setExtendedMode(enabled: Boolean) {
if (DisplayFlags.enableDisplayContentModeManagement()) {
- whenever(
- DesktopModeStatus.isDesktopModeSupportedOnDisplay(
- context,
- externalDisplay
- )
- ).thenReturn(enabled)
+ whenever(DesktopModeStatus.isDesktopModeSupportedOnDisplay(context, externalDisplay))
+ .thenReturn(enabled)
} else {
Settings.Global.putInt(
context.contentResolver,
@@ -329,9 +334,20 @@ class DesktopDisplayModeControllerTest(
}
}
- private fun setMouseConnected(connected: Boolean) {
- whenever(inputManager.inputDeviceIds)
- .thenReturn(if (connected) intArrayOf(EXTERNAL_DEVICE_ID) else intArrayOf())
+ private fun setTouchpadConnected(connected: Boolean) {
+ if (connected) {
+ connectedDeviceIds.add(TOUCHPAD_DEVICE_ID)
+ } else {
+ connectedDeviceIds.remove(TOUCHPAD_DEVICE_ID)
+ }
+ }
+
+ private fun setKeyboardConnected(connected: Boolean) {
+ if (connected) {
+ connectedDeviceIds.add(KEYBOARD_DEVICE_ID)
+ } else {
+ connectedDeviceIds.remove(KEYBOARD_DEVICE_ID)
+ }
}
private class ExtendedDisplaySettingsRestoreSession(
@@ -358,13 +374,8 @@ class DesktopDisplayModeControllerTest(
companion object {
const val EXTERNAL_DISPLAY_ID = 100
- const val EXTERNAL_DEVICE_ID = 10
-
- enum class SwitchState(val value: Int) {
- UNKNOWN(InputManager.SWITCH_STATE_UNKNOWN),
- ON(InputManager.SWITCH_STATE_ON),
- OFF(InputManager.SWITCH_STATE_OFF),
- }
+ const val TOUCHPAD_DEVICE_ID = 10
+ const val KEYBOARD_DEVICE_ID = 11
enum class ExternalDisplayBasedTargetModeTestCase(
val defaultWindowingMode: Int,
@@ -490,393 +501,265 @@ class DesktopDisplayModeControllerTest(
enum class FormFactorBasedTargetModeTestCase(
val hasExternalDisplay: Boolean,
val extendedDisplayEnabled: Boolean,
- val tabletModeStatus: SwitchState,
val isDefaultDisplayDesktopEligible: Boolean,
- val hasAnyMouseDevice: Boolean,
+ val hasAnyTouchpadDevice: Boolean,
+ val hasAnyKeyboardDevice: Boolean,
val expectedWindowingMode: Int,
) {
- EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- EXTERNAL_MIRROR_TABLET_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
+ EXTERNAL_EXTENDED_NO_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_EXTENDED_NO_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
+ EXTERNAL_MIRROR_NO_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_MIRROR_NO_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_NO_MOUSE(
+ EXTERNAL_EXTENDED_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_NO_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
- ),
- EXTERNAL_EXTENDED_TABLET_PROJECTED_NO_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_TABLET_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_EXTENDED_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_TABLET_PROJECTED_NO_MOUSE(
+ EXTERNAL_MIRROR_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_TABLET_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_MIRROR_PROJECTED_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_NO_MOUSE(
+ EXTERNAL_EXTENDED_NO_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_EXTENDED_NO_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_NO_MOUSE(
+ EXTERNAL_MIRROR_NO_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_MIRROR_NO_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_NO_MOUSE(
+ EXTERNAL_EXTENDED_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_EXTENDED_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN_PROJECTED_NO_MOUSE(
+ EXTERNAL_MIRROR_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED_NO_MOUSE(
+ NO_EXTERNAL_MIRROR_PROJECTED_NO_TOUCHPAD_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = false,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_MOUSE(
+ EXTERNAL_EXTENDED_NO_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_MOUSE(
+ NO_EXTERNAL_EXTENDED_NO_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- EXTERNAL_MIRROR_TABLET_NO_PROJECTED_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_MOUSE(
- hasExternalDisplay = true,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
- ),
- NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_MOUSE(
- hasExternalDisplay = false,
- extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
- isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_MOUSE(
+ EXTERNAL_MIRROR_NO_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_MOUSE(
+ NO_EXTERNAL_MIRROR_NO_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = true,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_TABLET_PROJECTED_MOUSE(
+ EXTERNAL_EXTENDED_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_TABLET_PROJECTED_MOUSE(
+ NO_EXTERNAL_EXTENDED_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_TABLET_PROJECTED_MOUSE(
+ EXTERNAL_MIRROR_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_TABLET_PROJECTED_MOUSE(
+ NO_EXTERNAL_MIRROR_PROJECTED_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = true,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_MOUSE(
+ EXTERNAL_EXTENDED_NO_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
- expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_MOUSE(
+ NO_EXTERNAL_EXTENDED_NO_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_MOUSE(
+ EXTERNAL_MIRROR_NO_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_MOUSE(
+ NO_EXTERNAL_MIRROR_NO_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.OFF,
- isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_MOUSE(
+ EXTERNAL_EXTENDED_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_MOUSE(
+ NO_EXTERNAL_EXTENDED_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN_PROJECTED_MOUSE(
+ EXTERNAL_MIRROR_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED_MOUSE(
+ NO_EXTERNAL_MIRROR_PROJECTED_NO_TOUCHPAD_NO_KEYBOARD(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
- tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
- hasAnyMouseDevice = true,
+ hasAnyTouchpadDevice = false,
+ hasAnyKeyboardDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index 652fae01c1b2..a4052890f08a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -283,14 +283,32 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
)
- fun testDefaultIndicators_bubblesEnabled() {
+ fun testDefaultIndicators_enableBubbleToFullscreen() {
createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
var result = visualIndicator.updateIndicatorType(PointF(10f, 1500f))
assertThat(result)
.isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_LEFT_INDICATOR)
- result = visualIndicator.updateIndicatorType(PointF(2300f, 1500f))
+ result = visualIndicator.updateIndicatorType(PointF(2390f, 1500f))
assertThat(result)
.isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR)
+
+ // Check that bubble zones are not available from split
+ createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
+ result = visualIndicator.updateIndicatorType(PointF(10f, 1500f))
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
+ result = visualIndicator.updateIndicatorType(PointF(2390f, 1500f))
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
+
+ // Check that bubble zones are not available from desktop
+ createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
+ result = visualIndicator.updateIndicatorType(PointF(10f, 1500f))
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
+ result = visualIndicator.updateIndicatorType(PointF(2390f, 1500f))
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
}
@Test
@@ -298,7 +316,7 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
)
- fun testDefaultIndicators_foldable_leftRightSplit() {
+ fun testDefaultIndicators_foldable_enableBubbleToFullscreen_dragFromFullscreen() {
setUpFoldable()
createVisualIndicator(
@@ -325,13 +343,47 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
result = visualIndicator.updateIndicatorType(foldRightBottom())
assertThat(result)
.isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR)
+ }
+
+ @Test
+ @EnableFlags(
+ com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
+ com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
+ )
+ @DisableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_ANYTHING)
+ fun testDefaultIndicators_foldable_enableBubbleToFullscreen_dragFromSplit() {
+ setUpFoldable()
createVisualIndicator(
DesktopModeVisualIndicator.DragStartState.FROM_SPLIT,
isSmallTablet = true,
isLeftRightSplit = true,
)
- result = visualIndicator.updateIndicatorType(foldCenter())
+ var result = visualIndicator.updateIndicatorType(foldCenter())
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+ // Check that bubbles are not available from split
+ result = visualIndicator.updateIndicatorType(foldLeftBottom())
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
+
+ result = visualIndicator.updateIndicatorType(foldRightBottom())
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
+ }
+
+ @Test
+ @EnableFlags(com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_ANYTHING)
+ fun testDefaultIndicators_foldable_enableBubbleAnything_dragFromSplit() {
+ setUpFoldable()
+
+ createVisualIndicator(
+ DesktopModeVisualIndicator.DragStartState.FROM_SPLIT,
+ isSmallTablet = true,
+ isLeftRightSplit = true,
+ )
+ var result = visualIndicator.updateIndicatorType(foldCenter())
assertThat(result)
.isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionControllerTest.kt
new file mode 100644
index 000000000000..47a9a6c8d840
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionControllerTest.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.os.Binder
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.view.Display.DEFAULT_DISPLAY
+import android.window.WindowContainerTransaction
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP
+import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.pip.PipDesktopState
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+/**
+ * Tests for [DesktopPipTransitionController].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DesktopPipTransitionControllerTest
+ */
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+class DesktopPipTransitionControllerTest(flags: FlagsParameterization) : ShellTestCase() {
+ private val mockDesktopTasksController = mock<DesktopTasksController>()
+ private val mockDesktopUserRepositories = mock<DesktopUserRepositories>()
+ private val mockDesktopRepository = mock<DesktopRepository>()
+ private val mockPipDesktopState = mock<PipDesktopState>()
+
+ private lateinit var controller: DesktopPipTransitionController
+
+ private val transition = Binder()
+ private val wct = WindowContainerTransaction()
+ private val taskInfo = createFreeformTask()
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Before
+ fun setUp() {
+ whenever(mockPipDesktopState.isDesktopWindowingPipEnabled()).thenReturn(true)
+ whenever(mockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mockDesktopRepository)
+ whenever(mockDesktopRepository.isAnyDeskActive(anyInt())).thenReturn(true)
+ whenever(mockDesktopRepository.getActiveDeskId(anyInt())).thenReturn(DESK_ID)
+
+ controller =
+ DesktopPipTransitionController(
+ mockDesktopTasksController,
+ mockDesktopUserRepositories,
+ mockPipDesktopState,
+ )
+ }
+
+ @Test
+ fun handlePipTransition_noDeskActive_doesntPerformDesktopExitCleanup() {
+ whenever(mockDesktopRepository.isAnyDeskActive(eq(taskInfo.displayId))).thenReturn(false)
+
+ controller.handlePipTransition(wct, transition, taskInfo)
+
+ verifyPerformDesktopExitCleanupAfterPip(isCalled = false)
+ }
+
+ @Test
+ fun handlePipTransition_notLastTask_doesntPerformDesktopExitCleanup() {
+ whenever(
+ mockDesktopRepository.isOnlyVisibleNonClosingTaskInDesk(
+ taskId = eq(taskInfo.taskId),
+ deskId = eq(DESK_ID),
+ displayId = eq(taskInfo.displayId),
+ )
+ )
+ .thenReturn(false)
+
+ controller.handlePipTransition(wct, transition, taskInfo)
+
+ verifyPerformDesktopExitCleanupAfterPip(isCalled = false)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun handlePipTransition_noActiveDeskId_multiDesk_doesntPerformDesktopExitCleanup() {
+ whenever(mockDesktopRepository.getActiveDeskId(eq(taskInfo.displayId))).thenReturn(null)
+
+ controller.handlePipTransition(wct, transition, taskInfo)
+
+ verifyPerformDesktopExitCleanupAfterPip(isCalled = false)
+ }
+
+ @Test
+ fun handlePipTransition_isLastTask_performDesktopExitCleanup() {
+ whenever(
+ mockDesktopRepository.isOnlyVisibleNonClosingTaskInDesk(
+ taskId = eq(taskInfo.taskId),
+ deskId = eq(DESK_ID),
+ displayId = eq(taskInfo.displayId),
+ )
+ )
+ .thenReturn(true)
+
+ controller.handlePipTransition(wct, transition, taskInfo)
+
+ verifyPerformDesktopExitCleanupAfterPip(isCalled = true)
+ }
+
+ private fun verifyPerformDesktopExitCleanupAfterPip(isCalled: Boolean) {
+ if (isCalled) {
+ verify(mockDesktopTasksController)
+ .performDesktopExitCleanUp(
+ wct = wct,
+ deskId = DESK_ID,
+ displayId = DEFAULT_DISPLAY,
+ willExitDesktop = true,
+ )
+ } else {
+ verify(mockDesktopTasksController, never())
+ .performDesktopExitCleanUp(
+ any(),
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyBoolean(),
+ )
+ }
+ }
+
+ private companion object {
+ const val DESK_ID = 1
+
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> =
+ FlagsParameterization.allCombinationsOf(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt
deleted file mode 100644
index ef394d81cc57..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopPipTransitionObserverTest.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.desktopmode
-
-import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
-import android.os.Binder
-import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
-import android.view.WindowManager.TRANSIT_PIP
-import android.window.TransitionInfo
-import androidx.test.filters.SmallTest
-import com.android.window.flags.Flags
-import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.TestRunningTaskInfoBuilder
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.mock
-
-/**
- * Tests for [DesktopPipTransitionObserver].
- *
- * Build/Install/Run: atest WMShellUnitTests:DesktopPipTransitionObserverTest
- */
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class DesktopPipTransitionObserverTest : ShellTestCase() {
-
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
- private lateinit var observer: DesktopPipTransitionObserver
-
- private val transition = Binder()
- private var onSuccessInvokedCount = 0
-
- @Before
- fun setUp() {
- observer = DesktopPipTransitionObserver()
-
- onSuccessInvokedCount = 0
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun onTransitionReady_taskInPinnedWindowingMode_onSuccessInvoked() {
- val taskId = 1
- val pipTransition = createPendingPipTransition(taskId)
- val successfulChange = createChange(taskId, WINDOWING_MODE_PINNED)
- observer.addPendingPipTransition(pipTransition)
-
- observer.onTransitionReady(
- transition = transition,
- info = TransitionInfo(
- TRANSIT_PIP, /* flags= */
- 0
- ).apply { addChange(successfulChange) },
- )
-
- assertThat(onSuccessInvokedCount).isEqualTo(1)
- }
-
- private fun createPendingPipTransition(
- taskId: Int
- ): DesktopPipTransitionObserver.PendingPipTransition {
- return DesktopPipTransitionObserver.PendingPipTransition(
- token = transition,
- taskId = taskId,
- onSuccess = { onSuccessInvokedCount += 1 },
- )
- }
-
- private fun createChange(taskId: Int, windowingMode: Int): TransitionInfo.Change {
- return TransitionInfo.Change(mock(), mock()).apply {
- taskInfo =
- TestRunningTaskInfoBuilder()
- .setTaskId(taskId)
- .setWindowingMode(windowingMode)
- .build()
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 3fb008377d6e..6ede990df15e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -1280,6 +1280,18 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun removeDesk_multipleDesks_active_marksInactiveInDisplay() {
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+ repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+
+ repo.removeDesk(deskId = 3)
+
+ assertThat(repo.getActiveDeskId(displayId = DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun removeDesk_multipleDesks_inactive_removes() {
repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
@@ -1292,6 +1304,18 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun removeDesk_multipleDesks_inactive_keepsOtherDeskActiveInDisplay() {
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+ repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+
+ repo.removeDesk(deskId = 2)
+
+ assertThat(repo.getActiveDeskId(displayId = DEFAULT_DISPLAY)).isEqualTo(3)
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
fun removeDesk_removesFromPersistence() =
runTest(StandardTestDispatcher()) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 71e46bcb0698..d81786b5e6a5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -32,9 +32,9 @@ import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.content.ComponentName
import android.content.Context
import android.content.Intent
-import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.pm.ActivityInfo
import android.content.pm.ActivityInfo.CONFIG_DENSITY
+import android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
@@ -50,6 +50,7 @@ import android.os.Binder
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
+import android.os.UserHandle
import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
@@ -265,7 +266,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Mock private lateinit var desksOrganizer: DesksOrganizer
@Mock private lateinit var userProfileContexts: UserProfileContexts
@Mock private lateinit var desksTransitionsObserver: DesksTransitionObserver
- @Mock private lateinit var desktopPipTransitionObserver: DesktopPipTransitionObserver
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var mockDisplayContext: Context
@Mock private lateinit var dragToDisplayTransitionHandler: DragToDisplayTransitionHandler
@@ -460,7 +460,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
overviewToDesktopTransitionObserver,
desksOrganizer,
desksTransitionsObserver,
- Optional.of(desktopPipTransitionObserver),
userProfileContexts,
desktopModeCompatPolicy,
dragToDisplayTransitionHandler,
@@ -1200,9 +1199,11 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
existingTask.topActivity = testComponent
existingTask.configuration.windowConfiguration.setBounds(Rect(0, 0, 500, 500))
// Set up new instance of already existing task.
- val launchingTask = setUpFullscreenTask()
+ val launchingTask =
+ setUpFullscreenTask().apply {
+ topActivityInfo = ActivityInfo().apply { launchMode = LAUNCH_SINGLE_INSTANCE }
+ }
launchingTask.topActivity = testComponent
- launchingTask.baseIntent.addFlags(FLAG_ACTIVITY_NEW_TASK)
// Move new instance to desktop. By default multi instance is not supported so first
// instance will close.
@@ -1224,10 +1225,12 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
existingTask.topActivity = testComponent
existingTask.configuration.windowConfiguration.setBounds(Rect(0, 0, 500, 500))
// Set up new instance of already existing task.
- val launchingTask = setUpFreeformTask(active = false)
+ val launchingTask =
+ setUpFreeformTask(active = false).apply {
+ topActivityInfo = ActivityInfo().apply { launchMode = LAUNCH_SINGLE_INSTANCE }
+ }
taskRepository.removeTask(launchingTask.displayId, launchingTask.taskId)
launchingTask.topActivity = testComponent
- launchingTask.baseIntent.addFlags(FLAG_ACTIVITY_NEW_TASK)
// Move new instance to desktop. By default multi instance is not supported so first
// instance will close.
@@ -1526,6 +1529,37 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ fun launchIntent_taskInDesktopMode_onSecondaryDisplay_transitionStarted() {
+ setUpLandscapeDisplay()
+ taskRepository.addDesk(SECOND_DISPLAY, deskId = 2)
+ val intent = Intent().setComponent(homeComponentName)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.startLaunchIntentTransition(intent, Bundle.EMPTY, SECOND_DISPLAY)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ // We expect two actions: open the app and start the desk
+ assertThat(wct.hierarchyOps).hasSize(2)
+ val hOps0 = wct.hierarchyOps[0]
+ val hOps1 = wct.hierarchyOps[1]
+ assertThat(hOps0.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+ val activityOptions0 = ActivityOptions.fromBundle(hOps0.launchOptions)
+ assertThat(activityOptions0.launchDisplayId).isEqualTo(SECOND_DISPLAY)
+ assertThat(hOps1.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+ val activityOptions1 = ActivityOptions.fromBundle(hOps1.launchOptions)
+ assertThat(activityOptions1.launchDisplayId).isEqualTo(SECOND_DISPLAY)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun addMoveToDeskTaskChanges_landscapeDevice_userFullscreenOverride_defaultPortraitBounds() {
setUpLandscapeDisplay()
@@ -2347,9 +2381,10 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
@DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun moveToFullscreen_tdaFreeform_windowingModeFullscreen_removesWallpaperActivity() {
+ whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context))
+ .thenReturn(false)
val homeTask = setUpHomeTask()
val task = setUpFreeformTask()
-
assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
.configuration
.windowConfiguration
@@ -2401,9 +2436,10 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
)
fun moveToFullscreen_tdaFreeform_windowingModeFullscreen_homeBehindFullscreen_multiDesksEnabled() {
+ whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context))
+ .thenReturn(false)
val homeTask = setUpHomeTask()
val task = setUpFreeformTask()
-
assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
.configuration
.windowConfiguration
@@ -3505,15 +3541,20 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
.addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0))
}
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun onPipTaskMinimize_autoEnterEnabled_startPipTransition() {
- val task = setUpPipTask(autoEnterEnabled = true)
+ private fun minimizePipTask(task: RunningTaskInfo) {
val handler = mock(TransitionHandler::class.java)
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+ fun onPipTaskMinimize_autoEnterEnabled_startPipTransition() {
+ val task = setUpPipTask(autoEnterEnabled = true)
+
+ minimizePipTask(task)
verify(freeformTaskTransitionStarter).startPipTransition(any())
verify(freeformTaskTransitionStarter, never())
@@ -3533,7 +3574,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
)
.thenReturn(Binder())
- controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+ minimizePipTask(task)
verify(freeformTaskTransitionStarter)
.startMinimizedModeTransition(any(), eq(task.taskId), anyBoolean())
@@ -3544,52 +3585,24 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
fun onPipTaskMinimize_autoEnterEnabled_sendsTaskbarRoundingUpdate() {
val task = setUpPipTask(autoEnterEnabled = true)
- val handler = mock(TransitionHandler::class.java)
- whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
- .thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
- controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+ minimizePipTask(task)
verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(anyBoolean())
}
@Test
@EnableFlags(
- Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP,
- )
- fun onDesktopTaskEnteredPip_pipIsLastTask_removesWallpaper() {
- val task = setUpPipTask(autoEnterEnabled = true)
-
- controller.onDesktopTaskEnteredPip(
- taskId = task.taskId,
- deskId = DEFAULT_DISPLAY,
- displayId = task.displayId,
- taskIsLastVisibleTaskBeforePip = true,
- )
-
- // Wallpaper is moved to the back
- val wct = getLatestTransition()
- wct.assertReorder(wallpaperToken, /* toTop= */ false)
- }
-
- @Test
- @EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP,
Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
)
- fun onDesktopTaskEnteredPip_pipIsLastTask_deactivatesDesk() {
+ fun onPipTaskMinimize_isLastTask_deactivatesDesk() {
val deskId = DEFAULT_DISPLAY
val task = setUpPipTask(autoEnterEnabled = true, deskId = deskId)
val transition = Binder()
- whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
+ whenever(freeformTaskTransitionStarter.startPipTransition(any())).thenReturn(transition)
- controller.onDesktopTaskEnteredPip(
- taskId = task.taskId,
- deskId = deskId,
- displayId = task.displayId,
- taskIsLastVisibleTaskBeforePip = true,
- )
+ minimizePipTask(task)
verify(desksOrganizer).deactivateDesk(any(), eq(deskId))
verify(desksTransitionsObserver)
@@ -3597,44 +3610,31 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun onDesktopTaskEnteredPip_pipIsLastTask_launchesHome() {
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ )
+ fun onPipTaskMinimize_isLastTask_removesWallpaper() {
val task = setUpPipTask(autoEnterEnabled = true)
- controller.onDesktopTaskEnteredPip(
- taskId = task.taskId,
- deskId = DEFAULT_DISPLAY,
- displayId = task.displayId,
- taskIsLastVisibleTaskBeforePip = true,
- )
+ minimizePipTask(task)
- val wct = getLatestTransition()
- wct.assertPendingIntent(launchHomeIntent(DEFAULT_DISPLAY))
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(freeformTaskTransitionStarter).startPipTransition(arg.capture())
+ // Wallpaper is moved to the back
+ arg.lastValue.assertReorder(wallpaperToken, /* toTop= */ false)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
- fun onDesktopTaskEnteredPip_pipIsNotLastTask_doesntExitDesktopMode() {
+ fun onPipTaskMinimize_isLastTask_launchesHome() {
val task = setUpPipTask(autoEnterEnabled = true)
- val deskId = DEFAULT_DISPLAY
- setUpFreeformTask(deskId = deskId) // launch another freeform task
- val transition = Binder()
- whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
- controller.onDesktopTaskEnteredPip(
- taskId = task.taskId,
- deskId = deskId,
- displayId = task.displayId,
- taskIsLastVisibleTaskBeforePip = false,
- )
+ minimizePipTask(task)
- // No transition to exit Desktop mode is started
- verifyWCTNotExecuted()
- verify(desktopModeEnterExitTransitionListener, never())
- .onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
- verify(desksOrganizer, never()).deactivateDesk(any(), eq(deskId))
- verify(desksTransitionsObserver, never())
- .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(freeformTaskTransitionStarter).startPipTransition(arg.capture())
+ arg.lastValue.assertPendingIntent(launchHomeIntent(DEFAULT_DISPLAY))
}
@Test
@@ -4252,6 +4252,25 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_fullscreenTask_noInDesk_enforceDesktop_secondaryDisplay_movesToDesk() {
+ val deskId = 5
+ taskRepository.addDesk(displayId = SECONDARY_DISPLAY_ID, deskId = deskId)
+ taskRepository.setDeskInactive(deskId)
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
+ whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+
+ val fullscreenTask = createFullscreenTask(displayId = SECONDARY_DISPLAY_ID)
+ val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+ assertNotNull(wct, "should handle request")
+ verify(desksOrganizer).moveTaskToDesk(wct, deskId, fullscreenTask)
+ }
+
+ @Test
fun handleRequest_fullscreenTask_notInDesk_enforceDesktop_fullscreenDisplay_returnNull() {
taskRepository.setDeskInactive(deskId = 0)
whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
@@ -5519,7 +5538,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
controller.removeDesk(deskId = 2)
- verify(desksOrganizer).removeDesk(any(), eq(2))
+ verify(desksOrganizer).removeDesk(any(), eq(2), any())
verify(desksTransitionsObserver)
.addPendingTransition(
argThat {
@@ -5535,6 +5554,49 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
)
+ fun removeDesk_multipleDesks_removesRunningTasks() {
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_CLOSE), any(), anyOrNull()))
+ .thenReturn(transition)
+ taskRepository.addDesk(DEFAULT_DISPLAY, deskId = 2)
+ val task1 = setUpFreeformTask(deskId = 2)
+ val task2 = setUpFreeformTask(deskId = 2)
+ val task3 = setUpFreeformTask(deskId = 2)
+
+ controller.removeDesk(deskId = 2)
+
+ val wct = getLatestWct(TRANSIT_CLOSE)
+ wct.assertRemove(task1.token)
+ wct.assertRemove(task2.token)
+ wct.assertRemove(task3.token)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun removeDesk_multipleDesks_removesRecentTasks() {
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_CLOSE), any(), anyOrNull()))
+ .thenReturn(transition)
+ taskRepository.addDesk(DEFAULT_DISPLAY, deskId = 2)
+ val task1 = setUpFreeformTask(deskId = 2, background = true)
+ val task2 = setUpFreeformTask(deskId = 2, background = true)
+ val task3 = setUpFreeformTask(deskId = 2, background = true)
+
+ controller.removeDesk(deskId = 2)
+
+ verify(recentTasksController).removeBackgroundTask(task1.taskId)
+ verify(recentTasksController).removeBackgroundTask(task2.taskId)
+ verify(recentTasksController).removeBackgroundTask(task3.taskId)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
fun activateDesk_multipleDesks_addsPendingTransition() {
val deskId = 0
val transition = Binder()
@@ -5608,6 +5670,33 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun activateDesk_otherDeskWasActive_deactivatesOtherDesk() {
+ val previouslyActiveDeskId = 1
+ val activatingDeskId = 0
+ val transition = Binder()
+ val deskChange = mock(TransitionInfo.Change::class.java)
+ whenever(transitions.startTransition(eq(TRANSIT_TO_FRONT), any(), anyOrNull()))
+ .thenReturn(transition)
+ whenever(desksOrganizer.isDeskActiveAtEnd(deskChange, activatingDeskId)).thenReturn(true)
+ // Make desk inactive by activating another desk.
+ taskRepository.addDesk(DEFAULT_DISPLAY, deskId = previouslyActiveDeskId)
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId = previouslyActiveDeskId)
+
+ controller.activateDesk(activatingDeskId, RemoteTransition(TestRemoteTransition()))
+
+ verify(desksOrganizer).deactivateDesk(any(), eq(previouslyActiveDeskId))
+ verify(desksTransitionsObserver)
+ .addPendingTransition(
+ argThat {
+ this is DeskTransition.DeactivateDesk &&
+ this.token == transition &&
+ this.deskId == previouslyActiveDeskId
+ }
+ )
+ }
+
+ @Test
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
@@ -7464,11 +7553,12 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun testCreateDesk() {
val currentDeskCount = taskRepository.getNumberOfDesks(DEFAULT_DISPLAY)
- whenever(desksOrganizer.createDesk(eq(DEFAULT_DISPLAY), any())).thenAnswer { invocation ->
- (invocation.arguments[1] as DesksOrganizer.OnCreateCallback).onCreated(deskId = 5)
+ whenever(desksOrganizer.createDesk(eq(DEFAULT_DISPLAY), any(), any())).thenAnswer {
+ invocation ->
+ (invocation.arguments[2] as DesksOrganizer.OnCreateCallback).onCreated(deskId = 5)
}
- controller.createDesk(DEFAULT_DISPLAY)
+ controller.createDesk(DEFAULT_DISPLAY, taskRepository.userId)
assertThat(taskRepository.getNumberOfDesks(DEFAULT_DISPLAY)).isEqualTo(currentDeskCount + 1)
}
@@ -7478,7 +7568,17 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
fun testCreateDesk_invalidDisplay_dropsRequest() {
controller.createDesk(INVALID_DISPLAY)
- verify(desksOrganizer, never()).createDesk(any(), any())
+ verify(desksOrganizer, never()).createDesk(any(), any(), any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testCreateDesk_systemUser_dropsRequest() {
+ assumeTrue(UserManager.isHeadlessSystemUserMode())
+
+ controller.createDesk(DEFAULT_DISPLAY, UserHandle.USER_SYSTEM)
+
+ verify(desksOrganizer, never()).createDesk(any(), any(), any())
}
@Test
@@ -7615,6 +7715,106 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
assertNull(latestWct.hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() })
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_returningToApp_noDeskDeactivation() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = true,
+ )
+
+ verify(desksOrganizer, never()).deactivateDesk(finishWct, deskId)
+ verify(desksTransitionsObserver, never())
+ .addPendingTransition(
+ argThat { t -> t.token == transition && t is DeskTransition.DeactivateDesk }
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_noActiveDesk_noDeskDeactivation() {
+ val deskId = 0
+ taskRepository.setDeskInactive(deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ verify(desksOrganizer, never()).deactivateDesk(finishWct, deskId)
+ verify(desksTransitionsObserver, never())
+ .addPendingTransition(
+ argThat { t -> t.token == transition && t is DeskTransition.DeactivateDesk }
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_activeDesk_notReturningToDesk_deactivatesDesk() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ verify(desksOrganizer).deactivateDesk(finishWct, deskId)
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_activeDesk_notReturningToDesk_notifiesDesktopExit() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_activeDesk_notReturningToDesk_doesNotBringUpWallpaperOrHome() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ finishWct.assertWithoutHop { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REORDER &&
+ hop.container == wallpaperToken.asBinder() &&
+ !hop.toTop
+ }
+ finishWct.assertWithoutHop { hop -> hop.type == HIERARCHY_OP_TYPE_PENDING_INTENT }
+ }
+
private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
var invocations = 0
private set
@@ -8111,6 +8311,15 @@ private fun WindowContainerTransaction.assertReorderSequenceInRange(
.inOrder()
}
+private fun WindowContainerTransaction.assertRemove(token: WindowContainerToken) {
+ assertThat(
+ hierarchyOps.any { hop ->
+ hop.container == token.asBinder() && hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK
+ }
+ )
+ .isTrue()
+}
+
private fun WindowContainerTransaction.assertRemoveAt(index: Int, token: WindowContainerToken) {
assertIndexInBounds(index)
val op = hierarchyOps[index]
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index 5ef1ace7873d..1e0c94c2452c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -50,7 +50,6 @@ import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import java.util.Optional
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -86,7 +85,6 @@ class DesktopTasksTransitionObserverTest {
private val userRepositories = mock<DesktopUserRepositories>()
private val taskRepository = mock<DesktopRepository>()
private val mixedHandler = mock<DesktopMixedTransitionHandler>()
- private val pipTransitionObserver = mock<DesktopPipTransitionObserver>()
private val backAnimationController = mock<BackAnimationController>()
private val desktopWallpaperActivityTokenProvider =
mock<DesktopWallpaperActivityTokenProvider>()
@@ -111,7 +109,6 @@ class DesktopTasksTransitionObserverTest {
transitions,
shellTaskOrganizer,
mixedHandler,
- Optional.of(pipTransitionObserver),
backAnimationController,
desktopWallpaperActivityTokenProvider,
shellInit,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
index 409ca57715fc..e55d7cbb73e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
@@ -264,6 +264,36 @@ class DesksTransitionObserverTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onTransitionFinish_deactivateDesk_updatesRepository() {
+ val transition = Binder()
+ val deactivateTransition = DeskTransition.DeactivateDesk(transition, deskId = 5)
+ repository.addDesk(DEFAULT_DISPLAY, deskId = 5)
+ repository.setActiveDesk(DEFAULT_DISPLAY, deskId = 5)
+
+ observer.addPendingTransition(deactivateTransition)
+ observer.onTransitionFinished(transition)
+
+ assertThat(repository.getActiveDeskId(DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onTransitionMergedAndFinished_deactivateDesk_updatesRepository() {
+ val transition = Binder()
+ val deactivateTransition = DeskTransition.DeactivateDesk(transition, deskId = 5)
+ repository.addDesk(DEFAULT_DISPLAY, deskId = 5)
+ repository.setActiveDesk(DEFAULT_DISPLAY, deskId = 5)
+
+ observer.addPendingTransition(deactivateTransition)
+ val bookEndTransition = Binder()
+ observer.onTransitionMerged(merged = transition, playing = bookEndTransition)
+ observer.onTransitionFinished(bookEndTransition)
+
+ assertThat(repository.getActiveDeskId(DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun onTransitionReady_twoPendingTransitions_handlesBoth() {
val transition = Binder()
// Active one desk and deactivate another in different displays, such as in some
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
index 9af504797182..34f832b4fba4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
@@ -15,10 +15,11 @@
*/
package com.android.wm.shell.desktopmode.multidesks
+import android.app.ActivityManager
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.testing.AndroidTestingRunner
-import android.view.Display
+import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.TransitionInfo
@@ -40,15 +41,17 @@ import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer.DeskRo
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellInit
import com.google.common.truth.Truth.assertThat
+import kotlin.coroutines.suspendCoroutine
import kotlin.test.assertNotNull
import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
import org.mockito.kotlin.argThat
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -67,6 +70,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
private val mockShellCommandHandler = mock<ShellCommandHandler>()
private val mockShellTaskOrganizer = mock<ShellTaskOrganizer>()
private val launchAdjacentController = LaunchAdjacentController(mock())
+ private val taskInfoChangedListener = mock<(ActivityManager.RunningTaskInfo) -> Unit>()
private lateinit var organizer: RootTaskDesksOrganizer
@@ -79,13 +83,24 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
mockShellTaskOrganizer,
launchAdjacentController,
)
+ organizer.setOnDesktopTaskInfoChangedListener(taskInfoChangedListener)
}
- @Test fun testCreateDesk_createsDeskAndMinimizationRoots() = runTest { createDesk() }
+ @Test fun testCreateDesk_createsDeskAndMinimizationRoots() = runTest { createDeskSuspending() }
+
+ @Test
+ fun testCreateDesk_rootExistsForOtherUser_reusesRoot() = runTest {
+ val desk = createDeskSuspending(userId = PRIMARY_USER_ID)
+
+ val deskId =
+ organizer.createDeskSuspending(displayId = DEFAULT_DISPLAY, userId = SECONDARY_USER_ID)
+
+ assertThat(deskId).isEqualTo(desk.deskRoot.deskId)
+ }
@Test
fun testCreateMinimizationRoot_marksHidden() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
verify(mockShellTaskOrganizer)
.applyTransaction(
@@ -110,7 +125,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testOnTaskAppeared_duplicateRoot_throws() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
assertThrows(Exception::class.java) {
organizer.onTaskAppeared(desk.deskRoot.taskInfo, SurfaceControl())
@@ -119,7 +134,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testOnTaskAppeared_duplicateMinimizedRoot_throws() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
assertThrows(Exception::class.java) {
organizer.onTaskAppeared(desk.minimizationRoot.taskInfo, SurfaceControl())
@@ -128,7 +143,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testOnTaskVanished_removesRoot() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
organizer.onTaskVanished(desk.deskRoot.taskInfo)
@@ -137,7 +152,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testOnTaskVanished_removesMinimizedRoot() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
organizer.onTaskVanished(desk.deskRoot.taskInfo)
organizer.onTaskVanished(desk.minimizationRoot.taskInfo)
@@ -147,7 +162,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testDesktopWindowAppearsInDesk() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
organizer.onTaskAppeared(child, SurfaceControl())
@@ -157,7 +172,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testDesktopWindowAppearsInDeskMinimizationRoot() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
organizer.onTaskAppeared(child, SurfaceControl())
@@ -167,7 +182,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testDesktopWindowMovesToMinimizationRoot() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
organizer.onTaskAppeared(child, SurfaceControl())
@@ -180,7 +195,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testDesktopWindowDisappearsFromDesk() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
organizer.onTaskAppeared(child, SurfaceControl())
@@ -191,7 +206,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testDesktopWindowDisappearsFromDeskMinimizationRoot() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
organizer.onTaskAppeared(child, SurfaceControl())
@@ -201,11 +216,23 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
+ fun testRemoveDesk_disablesAsLaunchRoot() = runTest {
+ val desk = createDeskSuspending(userId = PRIMARY_USER_ID)
+ val wct = WindowContainerTransaction()
+ organizer.activateDesk(wct, desk.deskRoot.deskId)
+ assertThat(desk.deskRoot.isLaunchRootRequested).isTrue()
+
+ organizer.removeDesk(wct, desk.deskRoot.deskId, userId = PRIMARY_USER_ID)
+
+ assertThat(desk.deskRoot.isLaunchRootRequested).isFalse()
+ }
+
+ @Test
fun testRemoveDesk_removesDeskRoot() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending(userId = PRIMARY_USER_ID)
val wct = WindowContainerTransaction()
- organizer.removeDesk(wct, desk.deskRoot.deskId)
+ organizer.removeDesk(wct, desk.deskRoot.deskId, userId = PRIMARY_USER_ID)
assertThat(
wct.hierarchyOps.any { hop ->
@@ -218,10 +245,10 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testRemoveDesk_removesMinimizationRoot() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending(userId = PRIMARY_USER_ID)
val wct = WindowContainerTransaction()
- organizer.removeDesk(wct, desk.deskRoot.deskId)
+ organizer.removeDesk(wct, desk.deskRoot.deskId, userId = PRIMARY_USER_ID)
assertThat(
wct.hierarchyOps.any { hop ->
@@ -233,8 +260,27 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
+ fun testRemoveDesk_rootUsedByOtherUser_keepsDeskRoot() = runTest {
+ val primaryUserDesk = createDeskSuspending(userId = PRIMARY_USER_ID)
+ val secondaryUserDesk = createDeskSuspending(userId = SECONDARY_USER_ID)
+ assertThat(primaryUserDesk).isEqualTo(secondaryUserDesk)
+
+ val wct = WindowContainerTransaction()
+ organizer.removeDesk(wct, primaryUserDesk.deskRoot.deskId, userId = PRIMARY_USER_ID)
+
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK &&
+ hop.container == primaryUserDesk.deskRoot.token.asBinder()
+ }
+ )
+ .isFalse()
+ assertThat(primaryUserDesk.deskRoot.users).containsExactly(SECONDARY_USER_ID)
+ }
+
+ @Test
fun testActivateDesk() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val wct = WindowContainerTransaction()
organizer.activateDesk(wct, desk.deskRoot.deskId)
@@ -266,7 +312,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testMoveTaskToDesk() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val desktopTask = createFreeformTask().apply { parentTaskId = -1 }
val wct = WindowContainerTransaction()
@@ -303,7 +349,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testGetDeskAtEnd() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val endDesk =
@@ -316,7 +362,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testGetDeskAtEnd_inMinimizationRoot() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
val endDesk =
@@ -329,7 +375,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun testIsDeskActiveAtEnd() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val isActive =
organizer.isDeskActiveAtEnd(
@@ -347,7 +393,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun deactivateDesk_clearsLaunchRoot() = runTest {
val wct = WindowContainerTransaction()
- val desk = createDesk()
+ val desk = createDeskSuspending()
organizer.activateDesk(wct, desk.deskRoot.deskId)
organizer.deactivateDesk(wct, desk.deskRoot.deskId)
@@ -365,7 +411,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun isDeskChange_forDeskId() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
assertThat(
organizer.isDeskChange(
@@ -380,7 +426,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun isDeskChange_forDeskId_inMinimizationRoot() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
assertThat(
organizer.isDeskChange(
@@ -398,7 +444,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun isDeskChange_anyDesk() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
assertThat(
organizer.isDeskChange(
@@ -412,7 +458,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun isDeskChange_anyDesk_inMinimizationRoot() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
assertThat(
organizer.isDeskChange(
@@ -429,7 +475,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun minimizeTask() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val wct = WindowContainerTransaction()
organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, task)
@@ -442,7 +488,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun minimizeTask_alreadyMinimized_noOp() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
val wct = WindowContainerTransaction()
organizer.onTaskAppeared(task, SurfaceControl())
@@ -454,8 +500,8 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun minimizeTask_inDifferentDesk_noOp() = runTest {
- val desk = createDesk()
- val otherDesk = createDesk()
+ val desk = createDeskSuspending()
+ val otherDesk = createDeskSuspending()
val task = createFreeformTask().apply { parentTaskId = otherDesk.deskRoot.deskId }
val wct = WindowContainerTransaction()
organizer.onTaskAppeared(task, SurfaceControl())
@@ -467,7 +513,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun unminimizeTask() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val wct = WindowContainerTransaction()
organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, task)
@@ -484,7 +530,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun unminimizeTask_alreadyUnminimized_noOp() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val wct = WindowContainerTransaction()
organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, task)
@@ -498,7 +544,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun unminimizeTask_notInDesk_noOp() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val task = createFreeformTask()
val wct = WindowContainerTransaction()
@@ -509,7 +555,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun reorderTaskToFront() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val wct = WindowContainerTransaction()
organizer.onTaskAppeared(task, SurfaceControl())
@@ -529,7 +575,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun reorderTaskToFront_notInDesk_noOp() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val task = createFreeformTask()
val wct = WindowContainerTransaction()
@@ -548,7 +594,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
@Test
fun reorderTaskToFront_minimized_unminimizesAndReorders() = runTest {
- val desk = createDesk()
+ val desk = createDeskSuspending()
val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val wct = WindowContainerTransaction()
organizer.onTaskAppeared(task, SurfaceControl())
@@ -573,7 +619,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun onTaskAppeared_visibleDesk_onlyDesk_disablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = true
- createDesk(visible = true)
+ createDeskSuspending(visible = true)
assertThat(launchAdjacentController.launchAdjacentEnabled).isFalse()
}
@@ -582,7 +628,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun onTaskAppeared_invisibleDesk_onlyDesk_enablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = false
- createDesk(visible = false)
+ createDeskSuspending(visible = false)
assertThat(launchAdjacentController.launchAdjacentEnabled).isTrue()
}
@@ -591,8 +637,8 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun onTaskAppeared_invisibleDesk_otherVisibleDesk_disablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = true
- createDesk(visible = true)
- createDesk(visible = false)
+ createDeskSuspending(visible = true)
+ createDeskSuspending(visible = false)
assertThat(launchAdjacentController.launchAdjacentEnabled).isFalse()
}
@@ -601,7 +647,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun onTaskInfoChanged_deskBecomesVisible_onlyDesk_disablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = true
- val desk = createDesk(visible = false)
+ val desk = createDeskSuspending(visible = false)
desk.deskRoot.taskInfo.isVisible = true
organizer.onTaskInfoChanged(desk.deskRoot.taskInfo)
@@ -612,7 +658,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun onTaskInfoChanged_deskBecomesInvisible_onlyDesk_enablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = false
- val desk = createDesk(visible = true)
+ val desk = createDeskSuspending(visible = true)
desk.deskRoot.taskInfo.isVisible = false
organizer.onTaskInfoChanged(desk.deskRoot.taskInfo)
@@ -623,8 +669,8 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun onTaskInfoChanged_deskBecomesInvisible_otherVisibleDesk_disablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = true
- createDesk(visible = true)
- val desk = createDesk(visible = true)
+ createDeskSuspending(visible = true)
+ val desk = createDeskSuspending(visible = true)
desk.deskRoot.taskInfo.isVisible = false
organizer.onTaskInfoChanged(desk.deskRoot.taskInfo)
@@ -635,7 +681,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun onTaskVanished_visibleDeskDisappears_onlyDesk_enablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = false
- val desk = createDesk(visible = true)
+ val desk = createDeskSuspending(visible = true)
organizer.onTaskVanished(desk.deskRoot.taskInfo)
assertThat(launchAdjacentController.launchAdjacentEnabled).isTrue()
@@ -645,19 +691,50 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun onTaskVanished_visibleDeskDisappears_otherDeskVisible_disablesLaunchAdjacent() = runTest {
launchAdjacentController.launchAdjacentEnabled = true
- createDesk(visible = true)
- val desk = createDesk(visible = true)
+ createDeskSuspending(visible = true)
+ val desk = createDeskSuspending(visible = true)
organizer.onTaskVanished(desk.deskRoot.taskInfo)
assertThat(launchAdjacentController.launchAdjacentEnabled).isFalse()
}
+ @Test
+ fun onTaskInfoChanged_taskNotRoot_invokesListener() = runTest {
+ createDeskSuspending()
+ val task = createFreeformTask().apply { taskId = TEST_CHILD_TASK_ID }
+
+ organizer.onTaskInfoChanged(task)
+
+ verify(taskInfoChangedListener).invoke(task)
+ }
+
+ @Test
+ fun onTaskInfoChanged_isDeskRoot_doesNotInvokeListener() = runTest {
+ val deskRoot = createDeskSuspending().deskRoot
+
+ organizer.onTaskInfoChanged(deskRoot.taskInfo)
+
+ verify(taskInfoChangedListener, never()).invoke(any())
+ }
+
+ @Test
+ fun onTaskInfoChanged_isMinimizationRoot_doesNotInvokeListener() = runTest {
+ val minimizationRoot = createDeskSuspending().minimizationRoot
+
+ organizer.onTaskInfoChanged(minimizationRoot.taskInfo)
+
+ verify(taskInfoChangedListener, never()).invoke(any())
+ }
+
private data class DeskRoots(
val deskRoot: DeskRoot,
val minimizationRoot: DeskMinimizationRoot,
)
- private suspend fun createDesk(visible: Boolean = true): DeskRoots {
+ private suspend fun createDeskSuspending(
+ visible: Boolean = true,
+ userId: Int = PRIMARY_USER_ID,
+ ): DeskRoots {
val freeformRootTask =
createFreeformTask().apply {
parentTaskId = -1
@@ -668,7 +745,7 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
Mockito.reset(mockShellTaskOrganizer)
whenever(
mockShellTaskOrganizer.createRootTask(
- Display.DEFAULT_DISPLAY,
+ DEFAULT_DISPLAY,
WINDOWING_MODE_FREEFORM,
organizer,
true,
@@ -682,13 +759,9 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
val listener = (invocation.arguments[2] as TaskListener)
listener.onTaskAppeared(minimizationRootTask, SurfaceControl())
}
- val deskId = organizer.createDesk(Display.DEFAULT_DISPLAY)
- assertEquals(freeformRootTask.taskId, deskId)
- val deskRoot = assertNotNull(organizer.deskRootsByDeskId.get(freeformRootTask.taskId))
- val minimizationRoot =
- assertNotNull(organizer.deskMinimizationRootsByDeskId[freeformRootTask.taskId])
- assertThat(minimizationRoot.deskId).isEqualTo(freeformRootTask.taskId)
- assertThat(minimizationRoot.rootId).isEqualTo(minimizationRootTask.taskId)
+ val deskId = organizer.createDeskSuspending(DEFAULT_DISPLAY, userId)
+ val deskRoot = assertNotNull(organizer.deskRootsByDeskId.get(deskId))
+ val minimizationRoot = assertNotNull(organizer.deskMinimizationRootsByDeskId[deskId])
return DeskRoots(deskRoot, minimizationRoot)
}
@@ -712,4 +785,15 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
hop.newParent == desk.deskRoot.token.asBinder() &&
hop.toTop
}
+
+ private suspend fun DesksOrganizer.createDeskSuspending(displayId: Int, userId: Int): Int =
+ suspendCoroutine { cont ->
+ createDesk(displayId, userId) { deskId -> cont.resumeWith(Result.success(deskId)) }
+ }
+
+ companion object {
+ private const val PRIMARY_USER_ID = 10
+ private const val SECONDARY_USER_ID = 11
+ private const val TEST_CHILD_TASK_ID = 100
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 714e5f486285..69a42164071b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -373,6 +373,25 @@ public class FreeformTaskTransitionObserverTest extends ShellTestCase {
verify(mDesksTransitionObserver).onTransitionReady(transition, info);
}
+ @Test
+ public void onTransitionMerged_forwardsToDesksTransitionObserver() {
+ final IBinder merged = mock(IBinder.class);
+ final IBinder playing = mock(IBinder.class);
+
+ mTransitionObserver.onTransitionMerged(merged, playing);
+
+ verify(mDesksTransitionObserver).onTransitionMerged(merged, playing);
+ }
+
+ @Test
+ public void onTransitionFinished_forwardsToDesksTransitionObserver() {
+ final IBinder transition = mock(IBinder.class);
+
+ mTransitionObserver.onTransitionFinished(transition, /* aborted = */ false);
+
+ verify(mDesksTransitionObserver).onTransitionFinished(transition);
+ }
+
private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) {
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = taskId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 077b35583e04..64bd86134d92 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -167,6 +167,7 @@ public class StageCoordinatorTests extends ShellTestCase {
private final TestShellExecutor mMainExecutor = new TestShellExecutor();
private final ShellExecutor mAnimExecutor = new TestShellExecutor();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ private final Handler mAnimHandler = mock(Handler.class);
private final DisplayAreaInfo mDisplayAreaInfo = new DisplayAreaInfo(new MockToken().token(),
DEFAULT_DISPLAY, 0);
private final ActivityManager.RunningTaskInfo mMainChildTaskInfo =
@@ -571,8 +572,7 @@ public class StageCoordinatorTests extends ShellTestCase {
}
@Test
- @DisableFlags({Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
- Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX})
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
public void testRequestEnterSplit_didNotEnterSplitSelect_doesNotApplyTransaction() {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mStageCoordinator.registerSplitSelectListener(
@@ -629,9 +629,9 @@ public class StageCoordinatorTests extends ShellTestCase {
private Transitions createTestTransitions() {
ShellInit shellInit = new ShellInit(mMainExecutor);
final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
- mTaskOrganizer, mTransactionPool, mock(DisplayController.class), mMainExecutor,
- mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
- mock(FocusTransitionObserver.class));
+ mTaskOrganizer, mTransactionPool, mock(DisplayController.class),
+ mDisplayInsetsController, mMainExecutor, mMainHandler, mAnimExecutor, mAnimHandler,
+ mock(HomeTransitionObserver.class), mock(FocusTransitionObserver.class));
shellInit.init();
return t;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index 3a455ba6b5df..f11839ad4e72 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -24,8 +24,12 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -77,16 +81,15 @@ public class TaskViewTransitionsTest extends ShellTestCase {
@Mock
TaskViewTaskController mTaskViewTaskController;
@Mock
- ActivityManager.RunningTaskInfo mTaskInfo;
- @Mock
WindowContainerToken mToken;
@Mock
ShellTaskOrganizer mOrganizer;
@Mock
SyncTransactionQueue mSyncQueue;
- Executor mExecutor = command -> command.run();
+ Executor mExecutor = Runnable::run;
+ ActivityManager.RunningTaskInfo mTaskInfo;
TaskViewRepository mTaskViewRepository;
TaskViewTransitions mTaskViewTransitions;
@@ -305,4 +308,66 @@ public class TaskViewTransitionsTest extends ShellTestCase {
verify(mTaskViewTaskController).setTaskNotFound();
}
+
+ @Test
+ public void updateBoundsForUnfold_taskNotFound_doesNothing() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.token = mock(WindowContainerToken.class);
+ taskInfo.taskId = 666;
+ Rect bounds = new Rect(100, 50, 200, 250);
+ SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishTransaction = mock(SurfaceControl.Transaction.class);
+ assertThat(
+ mTaskViewTransitions.updateBoundsForUnfold(bounds, startTransaction,
+ finishTransaction, taskInfo, mock(SurfaceControl.class)))
+ .isFalse();
+
+ verify(startTransaction, never()).reparent(any(), any());
+ }
+
+ @Test
+ public void updateBoundsForUnfold_noPendingTransition_doesNothing() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ Rect bounds = new Rect(100, 50, 200, 250);
+ mTaskViewTransitions.setTaskBounds(mTaskViewTaskController, bounds);
+ assertThat(mTaskViewTransitions.hasPending()).isFalse();
+
+ SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishTransaction = mock(SurfaceControl.Transaction.class);
+ assertThat(
+ mTaskViewTransitions.updateBoundsForUnfold(bounds, startTransaction,
+ finishTransaction, mTaskInfo, mock(SurfaceControl.class)))
+ .isFalse();
+ verify(startTransaction, never()).reparent(any(), any());
+ }
+
+ @Test
+ public void updateBoundsForUnfold() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ Rect bounds = new Rect(100, 50, 200, 250);
+ mTaskViewTransitions.updateVisibilityState(mTaskViewTaskController, /* visible= */ true);
+ mTaskViewTransitions.setTaskBounds(mTaskViewTaskController, bounds);
+ assertThat(mTaskViewTransitions.hasPending()).isTrue();
+
+ SurfaceControl.Transaction startTransaction = createMockTransaction();
+ SurfaceControl.Transaction finishTransaction = createMockTransaction();
+ assertThat(
+ mTaskViewTransitions.updateBoundsForUnfold(bounds, startTransaction,
+ finishTransaction, mTaskInfo, mock(SurfaceControl.class)))
+ .isTrue();
+ assertThat(mTaskViewRepository.byTaskView(mTaskViewTaskController).mBounds)
+ .isEqualTo(bounds);
+ }
+
+ private SurfaceControl.Transaction createMockTransaction() {
+ SurfaceControl.Transaction transaction = mock(SurfaceControl.Transaction.class);
+ when(transaction.reparent(any(), any())).thenReturn(transaction);
+ when(transaction.setPosition(any(), anyFloat(), anyFloat())).thenReturn(transaction);
+ when(transaction.setWindowCrop(any(), anyInt(), anyInt())).thenReturn(transaction);
+ return transaction;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
index 18fdbeff40f4..2dab39184247 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
@@ -53,6 +53,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.sysui.ShellInit;
@@ -78,6 +79,8 @@ public class DefaultTransitionHandlerTest extends ShellTestCase {
InstrumentationRegistry.getInstrumentation().getTargetContext();
private final DisplayController mDisplayController = mock(DisplayController.class);
+ private final DisplayInsetsController mDisplayInsetsController =
+ mock(DisplayInsetsController.class);
private final TransactionPool mTransactionPool = new MockTransactionPool();
private final TestShellExecutor mMainExecutor = new TestShellExecutor();
private final TestShellExecutor mAnimExecutor = new TestShellExecutor();
@@ -95,9 +98,10 @@ public class DefaultTransitionHandlerTest extends ShellTestCase {
mContext,
mShellInit);
mTransitionHandler = new DefaultTransitionHandler(
- mContext, mShellInit, mDisplayController,
+ mContext, mShellInit, mDisplayController, mDisplayInsetsController,
mTransactionPool, mMainExecutor, mMainHandler, mAnimExecutor,
- mRootTaskDisplayAreaOrganizer, mock(InteractionJankMonitor.class));
+ mock(Handler.class), mRootTaskDisplayAreaOrganizer,
+ mock(InteractionJankMonitor.class));
mShellInit.init();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 55bff09e0ae2..5d77766dc0db 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -61,6 +61,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.IHomeTransitionListener;
import com.android.wm.shell.shared.TransactionPool;
@@ -87,7 +88,10 @@ public class HomeTransitionObserverTest extends ShellTestCase {
private final ShellExecutor mAnimExecutor = new TestShellExecutor();
private final TestShellExecutor mMainExecutor = new TestShellExecutor();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ private final Handler mAnimHandler = mock(Handler.class);
private final DisplayController mDisplayController = mock(DisplayController.class);
+ private final DisplayInsetsController mDisplayInsetsController =
+ mock(DisplayInsetsController.class);
private IHomeTransitionListener mListener;
private Transitions mTransition;
@@ -98,10 +102,11 @@ public class HomeTransitionObserverTest extends ShellTestCase {
mListener = mock(IHomeTransitionListener.class);
when(mListener.asBinder()).thenReturn(mock(IBinder.class));
- mHomeTransitionObserver = new HomeTransitionObserver(mContext, mMainExecutor);
+ mHomeTransitionObserver = new HomeTransitionObserver(mContext, mMainExecutor,
+ mDisplayInsetsController, mock(ShellInit.class));
mTransition = new Transitions(mContext, mock(ShellInit.class), mock(ShellController.class),
- mOrganizer, mTransactionPool, mDisplayController, mMainExecutor,
- mMainHandler, mAnimExecutor, mHomeTransitionObserver,
+ mOrganizer, mTransactionPool, mDisplayController, mDisplayInsetsController,
+ mMainExecutor, mMainHandler, mAnimExecutor, mAnimHandler, mHomeTransitionObserver,
mock(FocusTransitionObserver.class));
mHomeTransitionObserver.setHomeTransitionListener(mTransition, mListener);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 9849b1174d8e..4dd9cab1d340 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -107,6 +107,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.recents.IRecentsAnimationRunner;
@@ -145,6 +146,9 @@ public class ShellTransitionTests extends ShellTestCase {
private final ShellExecutor mAnimExecutor = new TestShellExecutor();
private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ private final Handler mAnimHandler = mock(Handler.class);
+ private final DisplayInsetsController mDisplayInsets =
+ mock(DisplayInsetsController.class);
@Before
public void setUp() {
@@ -156,9 +160,9 @@ public class ShellTransitionTests extends ShellTestCase {
public void instantiate_addInitCallback() {
ShellInit shellInit = mock(ShellInit.class);
final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
- mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
- mock(FocusTransitionObserver.class));
+ mOrganizer, mTransactionPool, createTestDisplayController(), mDisplayInsets,
+ mMainExecutor, mMainHandler, mAnimExecutor, mAnimHandler,
+ mock(HomeTransitionObserver.class), mock(FocusTransitionObserver.class));
// One from Transitions, one from RootTaskDisplayAreaOrganizer
verify(shellInit).addInitCallback(any(), eq(t));
verify(shellInit).addInitCallback(any(), isA(RootTaskDisplayAreaOrganizer.class));
@@ -169,9 +173,9 @@ public class ShellTransitionTests extends ShellTestCase {
ShellInit shellInit = new ShellInit(mMainExecutor);
ShellController shellController = mock(ShellController.class);
final Transitions t = new Transitions(mContext, shellInit, shellController,
- mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
- mock(FocusTransitionObserver.class));
+ mOrganizer, mTransactionPool, createTestDisplayController(), mDisplayInsets,
+ mMainExecutor, mMainHandler, mAnimExecutor, mAnimHandler,
+ mock(HomeTransitionObserver.class), mock(FocusTransitionObserver.class));
shellInit.init();
verify(shellController, times(1)).addExternalInterface(
eq(IShellTransitions.DESCRIPTOR), any(), any());
@@ -1314,8 +1318,9 @@ public class ShellTransitionTests extends ShellTestCase {
ShellInit shellInit = new ShellInit(mMainExecutor);
final Transitions transitions =
new Transitions(mContext, shellInit, mock(ShellController.class), mOrganizer,
- mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
+ mTransactionPool, createTestDisplayController(), mDisplayInsets,
+ mMainExecutor, mMainHandler, mAnimExecutor, mAnimHandler,
+ mock(HomeTransitionObserver.class),
mock(FocusTransitionObserver.class));
final RecentTasksController mockRecentsTaskController = mock(RecentTasksController.class);
doReturn(mContext).when(mockRecentsTaskController).getContext();
@@ -1909,8 +1914,9 @@ public class ShellTransitionTests extends ShellTestCase {
private Transitions createTestTransitions() {
ShellInit shellInit = new ShellInit(mMainExecutor);
final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
- mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
+ mOrganizer, mTransactionPool, createTestDisplayController(), mDisplayInsets,
+ mMainExecutor, mMainHandler, mAnimExecutor, mAnimHandler,
+ mock(HomeTransitionObserver.class),
mock(FocusTransitionObserver.class));
shellInit.init();
return t;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index aad18cba4436..e28d0acb579f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -60,6 +60,7 @@ import org.mockito.InOrder;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.Executor;
public class UnfoldTransitionHandlerTest extends ShellTestCase {
@@ -98,7 +99,8 @@ public class UnfoldTransitionHandlerTest extends ShellTestCase {
mTransactionPool,
executor,
mHandler,
- mTransitions
+ mTransitions,
+ /* bubbleTaskUnfoldTransitionMerger= */ Optional.empty()
);
shellInit.init();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
index b1f92411c5a3..067dcec5d65d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelAppHandleOnlyTest.kt
@@ -28,7 +28,6 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
-import android.view.WindowManager.TRANSIT_CHANGE
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -110,7 +109,7 @@ class DesktopModeWindowDecorViewModelAppHandleOnlyTest :
onTaskOpening(task, taskSurface)
assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
- onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
+ onTaskChanging(task, taskSurface)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
verify(decoration).close()
@@ -166,7 +165,7 @@ class DesktopModeWindowDecorViewModelAppHandleOnlyTest :
setLargeScreen(false)
setUpMockDecorationForTask(task)
- onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
+ onTaskChanging(task, taskSurface)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index ad3426e82805..40aa41b2b72a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -51,7 +51,6 @@ import android.view.SurfaceView
import android.view.View
import android.view.ViewRootImpl
import android.view.WindowInsets.Type.statusBars
-import android.view.WindowManager.TRANSIT_CHANGE
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp
import androidx.test.filters.SmallTest
@@ -135,7 +134,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
task.setWindowingMode(WINDOWING_MODE_UNDEFINED)
task.setActivityType(ACTIVITY_TYPE_UNDEFINED)
- onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
+ onTaskChanging(task, taskSurface)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
verify(decoration).close()
@@ -150,12 +149,12 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
val taskSurface = SurfaceControl()
setUpMockDecorationForTask(task)
- onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
+ onTaskChanging(task, taskSurface)
assertFalse(windowDecorByTaskIdSpy.contains(task.taskId))
task.setWindowingMode(WINDOWING_MODE_FREEFORM)
task.setActivityType(ACTIVITY_TYPE_STANDARD)
- onTaskChanging(task, taskSurface, TRANSIT_CHANGE)
+ onTaskChanging(task, taskSurface)
assertTrue(windowDecorByTaskIdSpy.contains(task.taskId))
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 4c9c2f14d805..23994a2bd547 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -55,6 +55,7 @@ import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.compatui.api.CompatUIHandler
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler
import com.android.wm.shell.desktopmode.DesktopImmersiveController
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
@@ -66,6 +67,7 @@ import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository
import com.android.wm.shell.desktopmode.education.AppHandleEducationController
import com.android.wm.shell.desktopmode.education.AppToWebEducationController
+import com.android.wm.shell.desktopmode.multidesks.DesksOrganizer
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
@@ -144,6 +146,8 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mock<DesktopActivityOrientationChangeHandler>()
protected val mockMultiDisplayDragMoveIndicatorController =
mock<MultiDisplayDragMoveIndicatorController>()
+ protected val mockCompatUIHandler = mock<CompatUIHandler>()
+ protected val mockDesksOrganizer = mock<DesksOrganizer>()
protected val mockInputManager = mock<InputManager>()
private val mockTaskPositionerFactory =
mock<DesktopModeWindowDecorViewModel.TaskPositionerFactory>()
@@ -243,6 +247,8 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
desktopModeCompatPolicy,
mockTilingWindowDecoration,
mockMultiDisplayDragMoveIndicatorController,
+ mockCompatUIHandler,
+ mockDesksOrganizer
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -334,7 +340,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(),
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
- any(), any(), any(), any())
+ any(), any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.user).thenReturn(mockUserHandle)
@@ -359,14 +365,12 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected fun onTaskChanging(
task: RunningTaskInfo,
leash: SurfaceControl = SurfaceControl(),
- changeMode: Int
) {
desktopModeWindowDecorViewModel.onTaskChanging(
task,
leash,
StubTransaction(),
StubTransaction(),
- changeMode
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index dd777a5ed270..a0171ea04da3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -106,6 +106,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.CaptionState;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
@@ -173,8 +174,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
private static final boolean DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS = false;
- private static final boolean DEFAULT_IS_RECENTS_TRANSITION_RUNNING = false;
- private static final boolean DEFAULT_IS_MOVING_TO_BACK = false;
@Mock
@@ -246,6 +245,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Mock
private DesktopModeEventLogger mDesktopModeEventLogger;
@Mock
+ private DesktopModeUiEventLogger mDesktopModeUiEventLogger;
+ @Mock
private DesktopRepository mDesktopRepository;
@Mock
private WindowDecorTaskResourceLoader mMockTaskResourceLoader;
@@ -303,15 +304,15 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
when(mMockHandleMenuFactory.create(any(), any(), any(), any(), any(), anyInt(), any(),
anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
- any(), anyInt(), anyInt(), anyInt(), anyInt()))
+ anyBoolean(), any(), any(), anyInt(), anyInt(), anyInt(), anyInt()))
.thenReturn(mMockHandleMenu);
when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any(), anyInt()))
.thenReturn(false);
when(mMockAppHeaderViewHolderFactory
- .create(any(), any(), any(), any(), any(), any(), any(), any(), any()))
+ .create(any(), any(), any(), any(), any(), any(), any(), any(), any(), any()))
.thenReturn(mMockAppHeaderViewHolder);
when(mMockAppHandleViewHolderFactory
- .create(any(), any(), any(), any(), any()))
+ .create(any(), any(), any(), any(), any(), any()))
.thenReturn(mMockAppHandleViewHolder);
when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
@@ -440,9 +441,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
/* shouldIgnoreCornerRadius= */ true,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mCornerRadius).isEqualTo(INVALID_CORNER_RADIUS);
}
@@ -541,9 +540,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
/* shouldIgnoreCornerRadius= */ true,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mCornerRadiusId).isEqualTo(Resources.ID_NULL);
}
@@ -745,9 +742,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- /* shouldExcludeCaptionFromAppBounds */ true,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ /* shouldExcludeCaptionFromAppBounds */ true);
// Force consuming flags are disabled.
assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue();
@@ -782,9 +777,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue();
assertThat(
@@ -863,9 +856,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
// Takes status bar inset as padding, ignores caption bar inset.
assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50);
@@ -893,9 +884,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsInsetSource).isFalse();
}
@@ -922,9 +911,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
// Header is always shown because it's assumed the status bar is always visible.
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -951,9 +938,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -979,9 +964,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -1007,9 +990,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -1036,9 +1017,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
@@ -1057,9 +1036,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -1086,9 +1063,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isTrue();
}
@@ -1115,9 +1090,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
assertThat(relayoutParams.mIsCaptionVisible).isFalse();
}
@@ -1148,65 +1121,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
assertThat(relayoutParams.mAsyncViewHost).isFalse();
}
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
- public void updateRelayoutParams_handle_movingToBack_captionNotVisible() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final RelayoutParams relayoutParams = new RelayoutParams();
-
- DesktopModeWindowDecoration.updateRelayoutParams(
- relayoutParams,
- mTestableContext,
- taskInfo,
- mMockSplitScreenController,
- DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
- DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
- DEFAULT_IS_STATUSBAR_VISIBLE,
- DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
- DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
- DEFAULT_IS_DRAGGING,
- new InsetsState(),
- DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- /* isMovingToBack= */ true);
-
- assertThat(relayoutParams.mIsCaptionVisible).isFalse();
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_INPUT_LAYER_TRANSITION_FIX)
- public void updateRelayoutParams_handle_inRecentsTransition_captionNotVisible() {
- final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
- taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final RelayoutParams relayoutParams = new RelayoutParams();
-
- DesktopModeWindowDecoration.updateRelayoutParams(
- relayoutParams,
- mTestableContext,
- taskInfo,
- mMockSplitScreenController,
- DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
- DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
- DEFAULT_IS_STATUSBAR_VISIBLE,
- DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
- DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
- DEFAULT_IS_DRAGGING,
- new InsetsState(),
- DEFAULT_HAS_GLOBAL_FOCUS,
- mExclusionRegion,
- DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- /* isRecentsTransitionRunning= */ true,
- DEFAULT_IS_MOVING_TO_BACK);
-
- assertThat(relayoutParams.mIsCaptionVisible).isFalse();
- }
-
@Test
public void relayout_fullscreenTask_appliesTransactionImmediately() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -1450,6 +1364,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
anyBoolean()
);
// Run runnable to set captured link to used
@@ -1487,6 +1402,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
anyBoolean()
);
openInBrowserCaptor.getValue().invoke(new Intent(Intent.ACTION_MAIN, TEST_URI1));
@@ -1519,6 +1435,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
anyBoolean()
);
@@ -1584,6 +1501,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
closeClickListener.capture(),
any(),
anyBoolean()
@@ -1617,6 +1535,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
/* forceShowSystemBars= */ eq(true)
);
}
@@ -1790,9 +1709,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private void verifyHandleMenuCreated(@Nullable Uri uri) {
verify(mMockHandleMenuFactory).create(any(), any(), any(), any(), any(), anyInt(),
any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
- anyBoolean(), argThat(intent ->
+ anyBoolean(), anyBoolean(), argThat(intent ->
(uri == null && intent == null) || intent.getData().equals(uri)),
- anyInt(), anyInt(), anyInt(), anyInt());
+ any(), anyInt(), anyInt(), anyInt(), anyInt());
}
private void createMaximizeMenu(DesktopModeWindowDecoration decoration) {
@@ -1838,9 +1757,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
DEFAULT_HAS_GLOBAL_FOCUS,
mExclusionRegion,
DEFAULT_SHOULD_IGNORE_CORNER_RADIUS,
- DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
- DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
- DEFAULT_IS_MOVING_TO_BACK);
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS);
}
private DesktopModeWindowDecoration createWindowDecoration(
@@ -1889,7 +1806,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger,
- mDesktopModeCompatPolicy);
+ mDesktopModeUiEventLogger, mDesktopModeCompatPolicy);
windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
mMockTouchEventListener, mMockTouchEventListener);
windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
@@ -2015,7 +1932,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@NonNull Context decorWindowContext,
@NonNull Function2<? super Integer,? super Integer,? extends Point>
positionSupplier,
- @NonNull Supplier<SurfaceControl.Transaction> transactionSupplier) {
+ @NonNull Supplier<SurfaceControl.Transaction> transactionSupplier,
+ @NonNull DesktopModeUiEventLogger desktopModeUiEventLogger) {
return mMaximizeMenu;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
index 360099777bde..95ae73cde19e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeInputListenerTest.kt
@@ -40,6 +40,7 @@ import com.android.wm.shell.windowdecor.DragResizeInputListener.TaskResizeInputE
import com.google.common.truth.Truth.assertThat
import java.util.function.Consumer
import java.util.function.Supplier
+import kotlin.test.assertNotNull
import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
@@ -69,9 +70,12 @@ class DragResizeInputListenerTest : ShellTestCase() {
private val sinkInputChannel = mock<InputChannel>()
private val decorationSurface = SurfaceControl.Builder().setName("decoration surface").build()
private val createdSurfaces = ArrayList<SurfaceControl>()
+ private val removedSurfaces = ArrayList<SurfaceControl>()
@After
fun tearDown() {
+ createdSurfaces.clear()
+ removedSurfaces.clear()
decorationSurface.release()
}
@@ -217,6 +221,19 @@ class DragResizeInputListenerTest : ShellTestCase() {
assertThat(createdSurfaces[1].isValid).isFalse()
}
+ @Test
+ fun testClose_releasesDecorationSurfaceWithoutRemoval() {
+ val inputListener = create()
+ testBgExecutor.flushAll()
+ inputListener.close()
+ testMainExecutor.flushAll()
+ testBgExecutor.flushAll()
+
+ val decorationSurface = assertNotNull(createdSurfaces[0])
+ assertThat(decorationSurface.isValid).isFalse()
+ assertThat(removedSurfaces.contains(decorationSurface)).isFalse()
+ }
+
private fun verifyNoInputChannelGrantRequests() {
verify(mockWindowSession, never())
.grantInputChannel(
@@ -258,7 +275,10 @@ class DragResizeInputListenerTest : ShellTestCase() {
{
object : StubTransaction() {
override fun remove(sc: SurfaceControl): SurfaceControl.Transaction {
- return super.remove(sc).also { sc.release() }
+ return super.remove(sc).also {
+ sc.release()
+ removedSurfaces.add(sc)
+ }
}
}
},
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index 2e46f6312d03..b0b95c97ae18 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -43,6 +43,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED
@@ -95,6 +96,8 @@ class HandleMenuTest : ShellTestCase() {
private lateinit var mockTaskResourceLoader: WindowDecorTaskResourceLoader
@Mock
private lateinit var mockAppIcon: Bitmap
+ @Mock
+ private lateinit var mockDesktopModeUiEventLogger: DesktopModeUiEventLogger
private lateinit var handleMenu: HandleMenu
@@ -287,8 +290,10 @@ class HandleMenuTest : ShellTestCase() {
shouldShowManageWindowsButton = false,
shouldShowChangeAspectRatioButton = false,
shouldShowDesktopModeButton = true,
+ shouldShowRestartButton = true,
isBrowserApp = false,
null /* openInAppOrBrowserIntent */,
+ mockDesktopModeUiEventLogger,
captionWidth = HANDLE_WIDTH,
captionHeight = 50,
captionX = captionX,
@@ -304,6 +309,7 @@ class HandleMenuTest : ShellTestCase() {
onChangeAspectRatioClickListener = mock(),
openInAppOrBrowserClickListener = mock(),
onOpenByDefaultClickListener = mock(),
+ onRestartClickListener = mock(),
onCloseMenuClickListener = mock(),
onOutsideTouchListener = mock(),
forceShowSystemBars = forceShowSystemBars
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
index e5f8d7d34a47..0adca5e864bf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -587,6 +587,31 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
}
@Test
+ fun tilingDivider_shouldBeShown_whenTiledTasksBecomeVisible() {
+ val task1 = createVisibleTask()
+ val task2 = createVisibleTask()
+ val additionalTaskHelper: DesktopTilingWindowDecoration.AppResizingHelper = mock()
+ whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+ whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+ whenever(additionalTaskHelper.taskInfo).thenReturn(task2)
+ whenever(additionalTaskHelper.desktopModeWindowDecoration)
+ .thenReturn(desktopWindowDecoration)
+
+ tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+ tilingDecoration.rightTaskResizingHelper = additionalTaskHelper
+ tilingDecoration.desktopTilingDividerWindowManager = desktopTilingDividerWindowManager
+ val changeInfo = createTransitFrontTransition(task1, task2)
+ tilingDecoration.onTransitionReady(
+ transition = mock(),
+ info = changeInfo,
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(desktopTilingDividerWindowManager, times(1)).showDividerBar()
+ }
+
+ @Test
fun taskNotTiled_shouldNotBeRemoved_whenNotTiled() {
val task1 = createVisibleTask()
val task2 = createVisibleTask()
@@ -762,6 +787,30 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
)
}
+ private fun createTransitFrontTransition(
+ task1: RunningTaskInfo?,
+ task2: RunningTaskInfo?,
+ type: Int = TRANSIT_TO_FRONT,
+ ) =
+ TransitionInfo(type, /* flags= */ 0).apply {
+ addChange(
+ Change(mock(), mock()).apply {
+ mode = TRANSIT_TO_FRONT
+ parent = null
+ taskInfo = task1
+ flags = flags
+ }
+ )
+ addChange(
+ Change(mock(), mock()).apply {
+ mode = TRANSIT_TO_FRONT
+ parent = null
+ taskInfo = task2
+ flags = flags
+ }
+ )
+ }
+
companion object {
private val NON_STABLE_BOUNDS_MOCK = Rect(50, 55, 100, 100)
private val STABLE_BOUNDS_MOCK = Rect(0, 0, 100, 100)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
index 2c3009cb8dc4..e23019a59307 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
@@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.policy.SystemBarUtils
import com.android.wm.shell.R
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import org.junit.Before
import org.junit.runner.RunWith
@@ -57,6 +58,7 @@ class AppHandleViewHolderTest : ShellTestCase() {
private val mockWindowManagerWrapper = mock<WindowManagerWrapper>()
private val mockHandler = mock<Handler>()
private val mockTaskInfo = mock<RunningTaskInfo>()
+ private val mockDesktopModeUiEventLogger = mock<DesktopModeUiEventLogger>()
@Before
fun setup() {
@@ -92,7 +94,8 @@ class AppHandleViewHolderTest : ShellTestCase() {
mockOnTouchListener,
mockOnClickListener,
mockWindowManagerWrapper,
- mockHandler
+ mockHandler,
+ mockDesktopModeUiEventLogger,
)
}
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 714f6e41ff05..6caae4c7623e 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -82,6 +82,9 @@ struct FindEntryResult {
// The bitmask of configuration axis with which the resource value varies.
uint32_t type_flags;
+ // The bitmask of ResTable_entry flags
+ uint16_t entry_flags;
+
// The dynamic package ID map for the package from which this resource came from.
const DynamicRefTable* dynamic_ref_table;
@@ -879,10 +882,10 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
// if we don't have a result yet
if (!final_result ||
// or this config is better before the locale than the existing result
- result->config.isBetterThanBeforeLocale(final_result->config, desired_config) ||
+ result->config.isBetterThanBeforeLocale(final_result->config, *desired_config) ||
// or the existing config isn't better before locale and this one specifies a locale
// whereas the existing one doesn't
- (!final_result->config.isBetterThanBeforeLocale(result->config, desired_config)
+ (!final_result->config.isBetterThanBeforeLocale(result->config, *desired_config)
&& has_locale && !final_has_locale)) {
final_result = result.value();
final_overlaid = overlaid;
@@ -1031,6 +1034,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
.entry = *entry,
.config = *best_config,
.type_flags = type_flags,
+ .entry_flags = (*best_entry_verified)->flags(),
.dynamic_ref_table = package_group.dynamic_ref_table.get(),
.package_name = &best_package->GetPackageName(),
.type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1),
@@ -1185,16 +1189,16 @@ base::expected<AssetManager2::SelectedValue, NullOrIOError> AssetManager2::GetRe
}
// Create a reference since we can't represent this complex type as a Res_value.
- return SelectedValue(Res_value::TYPE_REFERENCE, resid, result->cookie, result->type_flags,
- resid, result->config);
+ return SelectedValue(Res_value::TYPE_REFERENCE, resid, result->cookie, result->entry_flags,
+ result->type_flags, resid, result->config);
}
// Convert the package ID to the runtime assigned package ID.
Res_value value = std::get<Res_value>(result->entry);
result->dynamic_ref_table->lookupResourceValue(&value);
- return SelectedValue(value.dataType, value.data, result->cookie, result->type_flags,
- resid, result->config);
+ return SelectedValue(value.dataType, value.data, result->cookie, result->entry_flags,
+ result->type_flags, resid, result->config);
}
base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference(
@@ -1847,8 +1851,8 @@ std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid)
}
return AssetManager2::SelectedValue(entry_it->value.dataType, entry_it->value.data,
- entry_it->cookie, type_spec_flags, 0U /* resid */,
- {} /* config */);
+ entry_it->cookie, 0U /* entry flags*/, type_spec_flags,
+ 0U /* resid */, {} /* config */);
}
return std::nullopt;
}
diff --git a/libs/androidfw/LocaleDataLookup.cpp b/libs/androidfw/LocaleDataLookup.cpp
index 9aacdcb9ca92..ed645826234d 100644
--- a/libs/androidfw/LocaleDataLookup.cpp
+++ b/libs/androidfw/LocaleDataLookup.cpp
@@ -5774,7 +5774,6 @@ const char* lookupLikelyScript(uint32_t packed_lang_region) {
case 0xD2120000u: // squ -> Latn
case 0x73724D45u: // sr-ME -> Latn
case 0x7372524Fu: // sr-RO -> Latn
- case 0x73725255u: // sr-RU -> Latn
case 0x73725452u: // sr-TR -> Latn
case 0x82320000u: // sra -> Latn
case 0x92320000u: // sre -> Latn
@@ -7265,7 +7264,6 @@ const char* lookupLikelyScript(uint32_t packed_lang_region) {
return SCRIPT_CODES[73u];
case 0x8ACD0000u: // nwc -> Newa
return SCRIPT_CODES[74u];
- case 0xB40C474Eu: // man-GN -> Nkoo
case 0xBA0D0000u: // nqo -> Nkoo
return SCRIPT_CODES[75u];
case 0xDCF90000u: // zhx -> Nshu
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 8ecd6ba9b253..6ec605c2ced5 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2615,16 +2615,14 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
}
bool ResTable_config::isBetterThanBeforeLocale(const ResTable_config& o,
- const ResTable_config* requested) const {
- if (requested) {
- if (imsi || o.imsi) {
- if ((mcc != o.mcc) && requested->mcc) {
- return (mcc);
- }
+ const ResTable_config& requested) const {
+ if (imsi || o.imsi) {
+ if ((mcc != o.mcc) && requested.mcc) {
+ return mcc;
+ }
- if ((mnc != o.mnc) && requested->mnc) {
- return (mnc);
- }
+ if ((mnc != o.mnc) && requested.mnc) {
+ return mnc;
}
}
return false;
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index b0179524f6cd..ffcef944a6ba 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -257,6 +257,7 @@ class AssetManager2 {
: cookie(entry.cookie),
data(entry.value.data),
type(entry.value.dataType),
+ entry_flags(0U),
flags(bag->type_spec_flags),
resid(0U),
config() {
@@ -271,6 +272,9 @@ class AssetManager2 {
// Type of the data value.
uint8_t type;
+ // The bitmask of ResTable_entry flags
+ uint16_t entry_flags;
+
// The bitmask of configuration axis that this resource varies with.
// See ResTable_config::CONFIG_*.
uint32_t flags;
@@ -283,9 +287,10 @@ class AssetManager2 {
private:
SelectedValue(uint8_t value_type, Res_value::data_type value_data, ApkAssetsCookie cookie,
- uint32_t type_flags, uint32_t resid, ResTable_config config) :
- cookie(cookie), data(value_data), type(value_type), flags(type_flags),
- resid(resid), config(std::move(config)) {}
+ uint16_t entry_flags, uint32_t type_flags, uint32_t resid, ResTable_config config)
+ :
+ cookie(cookie), data(value_data), type(value_type), entry_flags(entry_flags),
+ flags(type_flags), resid(resid), config(std::move(config)) {}
};
// Retrieves the best matching resource value with ID `resid`.
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 63b28da075cd..bd72d3741460 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1416,7 +1416,10 @@ struct ResTable_config
// match the requested configuration at all.
bool isLocaleBetterThan(const ResTable_config& o, const ResTable_config* requested) const;
- bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const;
+ // The first part of isBetterThan() that only compares the fields that are higher priority than
+ // the locale. Use it when you need to do custom locale matching to filter out the configs prior
+ // to that.
+ bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config& requested) const;
String8 toString() const;
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 3f228841f6ba..948437230ecc 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -23,6 +23,7 @@
#include "androidfw/ResourceUtils.h"
#include "data/appaslib/R.h"
#include "data/basic/R.h"
+#include "data/flagged/R.h"
#include "data/lib_one/R.h"
#include "data/lib_two/R.h"
#include "data/libclient/R.h"
@@ -32,6 +33,7 @@
namespace app = com::android::app;
namespace appaslib = com::android::appaslib::app;
namespace basic = com::android::basic;
+namespace flagged = com::android::flagged;
namespace lib_one = com::android::lib_one;
namespace lib_two = com::android::lib_two;
namespace libclient = com::android::libclient;
@@ -87,6 +89,10 @@ class AssetManager2Test : public ::testing::Test {
overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk");
ASSERT_THAT(overlayable_assets_, NotNull());
+
+ flagged_assets_ = ApkAssets::Load("flagged/flagged.apk");
+ ASSERT_THAT(app_assets_, NotNull());
+
chdir(original_path.c_str());
}
@@ -104,6 +110,7 @@ class AssetManager2Test : public ::testing::Test {
AssetManager2::ApkAssetsPtr app_assets_;
AssetManager2::ApkAssetsPtr overlay_assets_;
AssetManager2::ApkAssetsPtr overlayable_assets_;
+ AssetManager2::ApkAssetsPtr flagged_assets_;
};
TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
@@ -856,4 +863,12 @@ TEST_F(AssetManager2Test, GetApkAssets) {
EXPECT_EQ(1, lib_one_assets_->getStrongCount());
}
+TEST_F(AssetManager2Test, GetFlaggedAssets) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({flagged_assets_});
+ auto value = assetmanager.GetResource(flagged::R::xml::flagged, false, 0);
+ ASSERT_TRUE(value.has_value());
+ EXPECT_TRUE(value->entry_flags & ResTable_entry::FLAG_USES_FEATURE_FLAGS);
+}
+
} // namespace android
diff --git a/libs/androidfw/tests/data/flagged/AndroidManifest.xml b/libs/androidfw/tests/data/flagged/AndroidManifest.xml
new file mode 100644
index 000000000000..cc1394328797
--- /dev/null
+++ b/libs/androidfw/tests/data/flagged/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.basic">
+ <application />
+</manifest>
diff --git a/libs/androidfw/tests/data/flagged/R.h b/libs/androidfw/tests/data/flagged/R.h
new file mode 100644
index 000000000000..33ccab28cdd3
--- /dev/null
+++ b/libs/androidfw/tests/data/flagged/R.h
@@ -0,0 +1,35 @@
+/*
+* Copyright (C) 2025 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include <cstdint>
+
+namespace com {
+namespace android {
+namespace flagged {
+
+struct R {
+ struct xml {
+ enum : uint32_t {
+ flagged = 0x7f010000,
+ };
+ };
+};
+
+} // namespace flagged
+} // namespace android
+} // namespace com \ No newline at end of file
diff --git a/libs/androidfw/tests/data/flagged/build b/libs/androidfw/tests/data/flagged/build
new file mode 100755
index 000000000000..9e5d21ba1833
--- /dev/null
+++ b/libs/androidfw/tests/data/flagged/build
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright (C) 2025 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
+
+aapt2 compile --dir res -o compiled.flata
+aapt2 link -o flagged.apk \
+ --manifest AndroidManifest.xml \
+ -I $PATH_TO_FRAMEWORK_RES \
+ -I ../basic/basic.apk \
+ compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/flagged/flagged.apk b/libs/androidfw/tests/data/flagged/flagged.apk
new file mode 100644
index 000000000000..94b8f4d9fcf0
--- /dev/null
+++ b/libs/androidfw/tests/data/flagged/flagged.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/flagged/res/xml/flagged.xml b/libs/androidfw/tests/data/flagged/res/xml/flagged.xml
new file mode 100644
index 000000000000..5fe8d1b3ac27
--- /dev/null
+++ b/libs/androidfw/tests/data/flagged/res/xml/flagged.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<first xmlns:android="http://schemas.android.com/apk/res/android">
+ <second android:featureFlag="android.content.res.always_false"/>
+</first> \ No newline at end of file
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ab1be7e6128d..1bde5ff43aa8 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -168,6 +168,14 @@ cc_defaults {
"libutils",
],
},
+ host_linux: {
+ shared_libs: [
+ "libaconfig_storage_read_api_cc",
+ ],
+ whole_static_libs: [
+ "hwui_flags_cc_lib",
+ ],
+ },
},
}
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 3ef970830dc4..46dcc1f4c50b 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -164,8 +164,13 @@ std::string Bitmap::getAshmemId(const char* tag, uint64_t bitmapId,
android::base::ReadFileToString("/proc/self/cmdline", &temp);
return temp;
}();
- return std::format("bitmap/{}-id_{}-{}x{}-size_{}-{}",
- tag, bitmapId, width, height, size, sCmdline);
+ /* counter is to ensure the uniqueness of the ashmem filename,
+ * e.g. a bitmap with same mId could be sent multiple times, an
+ * ashmem region is created each time
+ */
+ static std::atomic<uint32_t> counter{0};
+ return std::format("bitmap/{}_{}_{}x{}_size-{}_id-{}_{}",
+ tag, counter.fetch_add(1), width, height, size, bitmapId, sCmdline);
}
sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) {
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 104ece6582f5..1a258e022dd0 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -2,6 +2,9 @@
#include "Bitmap.h"
#include <android-base/unique_fd.h>
+#ifdef __linux__
+#include <com_android_graphics_hwui_flags.h>
+#endif
#include <hwui/Bitmap.h>
#include <hwui/Paint.h>
#include <inttypes.h>
@@ -28,17 +31,10 @@
#include "SkRefCnt.h"
#include "SkStream.h"
#include "SkTypes.h"
+#ifdef __linux__ // Only Linux support parcel
#include "android/binder_parcel.h"
-#include "android_nio_utils.h"
-
-#ifdef __ANDROID__
-#include <com_android_graphics_hwui_flags.h>
-namespace hwui_flags = com::android::graphics::hwui::flags;
-#else
-namespace hwui_flags {
-constexpr bool bitmap_parcel_ashmem_as_immutable() { return false; }
-}
#endif
+#include "android_nio_utils.h"
#define DEBUG_PARCEL 0
@@ -851,6 +847,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
#endif
}
+#ifdef __linux__ // Only Linux support parcel
// Returns whether this bitmap should be written to the parcel as mutable.
static bool shouldParcelAsMutable(SkBitmap& bitmap, AParcel* parcel) {
// If the bitmap is immutable, then parcel as immutable.
@@ -858,7 +855,7 @@ static bool shouldParcelAsMutable(SkBitmap& bitmap, AParcel* parcel) {
return false;
}
- if (!hwui_flags::bitmap_parcel_ashmem_as_immutable()) {
+ if (!com::android::graphics::hwui::flags::bitmap_parcel_ashmem_as_immutable()) {
return true;
}
@@ -867,6 +864,7 @@ static bool shouldParcelAsMutable(SkBitmap& bitmap, AParcel* parcel) {
// writing it to the parcel.
return !shouldUseAshmem(parcel, bitmap.computeByteSize());
}
+#endif
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jint density,
jobject parcel) {
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
index 476b6fda5007..6c82aa1ca27d 100644
--- a/libs/hwui/jni/GIFMovie.cpp
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -63,7 +63,7 @@ GIFMovie::GIFMovie(SkStream* stream)
}
fCurrIndex = -1;
fLastDrawIndex = -1;
- fPaintingColor = SK_AlphaTRANSPARENT;
+ fPaintingColor = SkPackARGB32(0, 0, 0, 0);
}
GIFMovie::~GIFMovie()
@@ -127,7 +127,7 @@ static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObje
for (; width > 0; width--, src++, dst++) {
if (*src != transparent && *src < cmap->ColorCount) {
const GifColorType& col = cmap->Colors[*src];
- *dst = SkColorSetRGB(col.Red, col.Green, col.Blue);
+ *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
}
}
}
@@ -395,10 +395,10 @@ bool GIFMovie::onGetBitmap(SkBitmap* bm)
lastIndex = fGIF->ImageCount - 1;
}
- SkColor bgColor = SK_ColorTRANSPARENT;
+ SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) {
const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor];
- bgColor = SkColorSetRGB(col.Red, col.Green, col.Blue);
+ bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
}
// draw each frames - not intelligent way
@@ -411,7 +411,7 @@ bool GIFMovie::onGetBitmap(SkBitmap* bm)
if (!trans && gif->SColorMap != nullptr) {
fPaintingColor = bgColor;
} else {
- fPaintingColor = SK_ColorTRANSPARENT;
+ fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
}
bm->eraseColor(fPaintingColor);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 0cd9c53c830f..e5a6260cfd98 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -44,7 +44,7 @@ namespace uirenderer {
namespace renderthread {
// Not all of these are strictly required, but are all enabled if present.
-static std::array<std::string_view, 23> sEnableExtensions{
+static std::array<std::string_view, 25> sEnableExtensions{
VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
@@ -68,6 +68,8 @@ static std::array<std::string_view, 23> sEnableExtensions{
VK_EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME,
VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME,
VK_EXT_DEVICE_FAULT_EXTENSION_NAME,
+ VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME,
+ VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME,
};
static bool shouldEnableExtension(const std::string_view& extension) {
@@ -746,7 +748,14 @@ VulkanManager::VkDrawResult VulkanManager::finishFrame(SkSurface* surface) {
ALOGE_IF(!context, "Surface is not backed by gpu");
GrSemaphoresSubmitted submitted = context->flush(
surface, SkSurfaces::BackendSurfaceAccess::kPresent, flushInfo);
- context->submit();
+
+ static uint64_t currentFrameID = 0;
+ GrSubmitInfo submitInfo;
+ if (!mFrameBoundaryANDROID) {
+ submitInfo.fMarkBoundary = GrMarkFrameBoundary::kYes;
+ submitInfo.fFrameID = currentFrameID++;
+ }
+ context->submit(submitInfo);
VkDrawResult drawResult{
.submissionTime = systemTime(),
};
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index a593ec6f8351..744211e39f79 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -30,14 +30,10 @@
// VK_ANDROID_frame_boundary is a bespoke extension defined by AGI
// (https://github.com/google/agi) to enable profiling of apps rendering via
// HWUI. This extension is not defined in Khronos, hence the need to declare it
-// manually here. There's a superseding extension (VK_EXT_frame_boundary) being
-// discussed in Khronos, but in the meantime we use the bespoke
-// VK_ANDROID_frame_boundary. This is a device extension that is implemented by
+// manually here. There's an extension (VK_EXT_frame_boundary) which we will use
+// instead if available. This is a device extension that is implemented by
// AGI's Vulkan capture layer, such that it is only supported by devices when
// AGI is doing a capture of the app.
-//
-// TODO(b/182165045): use the Khronos blessed VK_EXT_frame_boudary once it has
-// landed in the spec.
typedef void(VKAPI_PTR* PFN_vkFrameBoundaryANDROID)(VkDevice device, VkSemaphore semaphore,
VkImage image);
#define VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME "VK_ANDROID_frame_boundary"