summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java49
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java51
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java14
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java45
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java6
-rw-r--r--libs/WindowManager/Shell/Android.bp13
-rw-r--r--libs/WindowManager/Shell/res/drawable/bubble_manage_menu_section.xml24
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml21
-rw-r--r--libs/WindowManager/Shell/res/drawable/ic_expand_less.xml25
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml41
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml77
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml1
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml3
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java145
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt72
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java177
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java182
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java114
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java77
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java145
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java242
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/HandleView.java42
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt90
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java)40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt)113
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt)2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java79
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt417
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt154
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java104
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java56
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java132
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java69
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt31
-rw-r--r--libs/WindowManager/Shell/tests/flicker/Android.bp99
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTest.xml53
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml99
-rw-r--r--libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml (renamed from libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml)12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt76
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt55
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt276
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt97
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt28
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt25
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt53
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt19
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt26
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt59
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt14
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt19
-rw-r--r--libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto75
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java15
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockToken.java (renamed from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java)8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataRepositoryTest.kt190
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java67
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/LaunchAdjacentControllerTest.kt172
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java123
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt91
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt133
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt38
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt61
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt63
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java17
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.cpp20
-rw-r--r--libs/hwui/pipeline/skia/ShaderCache.h36
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp27
-rw-r--r--libs/hwui/renderthread/CanvasContext.h6
-rw-r--r--libs/hwui/tests/unit/ShaderCacheTests.cpp11
205 files changed, 5485 insertions, 1439 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 89f4890c254e..4cedd41e2d9a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -308,7 +308,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
forAllTaskContainers(taskContainer -> {
synchronized (mLock) {
- final List<TaskFragmentContainer> containers = taskContainer.mContainers;
+ final List<TaskFragmentContainer> containers =
+ taskContainer.getTaskFragmentContainers();
// Clean up the TaskFragmentContainers by the z-order from the lowest.
for (int i = 0; i < containers.size(); i++) {
final TaskFragmentContainer container = containers.get(i);
@@ -611,8 +612,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@NonNull TaskContainer taskContainer) {
// Update all TaskFragments in the Task. Make a copy of the list since some may be
// removed on updating.
- final List<TaskFragmentContainer> containers =
- new ArrayList<>(taskContainer.mContainers);
+ final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
for (int i = containers.size() - 1; i >= 0; i--) {
final TaskFragmentContainer container = containers.get(i);
// Wait until onTaskFragmentAppeared to update new container.
@@ -1331,7 +1331,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Check pending appeared activity first because there can be a delay for the server
// update.
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
+ final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
+ .getTaskFragmentContainers();
for (int j = containers.size() - 1; j >= 0; j--) {
final TaskFragmentContainer container = containers.get(j);
if (container.hasPendingAppearedActivity(activityToken)) {
@@ -1342,7 +1343,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Check appeared activity if there is no such pending appeared activity.
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
+ final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
+ .getTaskFragmentContainers();
for (int j = containers.size() - 1; j >= 0; j--) {
final TaskFragmentContainer container = containers.get(j);
if (container.hasAppearedActivity(activityToken)) {
@@ -1418,7 +1420,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
removeExistingSecondaryContainers(wct, primaryContainer);
}
- primaryContainer.getTaskContainer().mSplitContainers.add(splitContainer);
+ primaryContainer.getTaskContainer().addSplitContainer(splitContainer);
}
/** Cleanups all the dependencies when the TaskFragment is entering PIP. */
@@ -1430,8 +1432,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return;
}
final List<SplitContainer> splitsToRemove = new ArrayList<>();
+ final List<SplitContainer> splitContainers = taskContainer.getSplitContainers();
final Set<TaskFragmentContainer> containersToUpdate = new ArraySet<>();
- for (SplitContainer splitContainer : taskContainer.mSplitContainers) {
+ for (SplitContainer splitContainer : splitContainers) {
if (splitContainer.getPrimaryContainer() != container
&& splitContainer.getSecondaryContainer() != container) {
continue;
@@ -1449,7 +1452,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
container.resetDependencies();
- taskContainer.mSplitContainers.removeAll(splitsToRemove);
+ taskContainer.removeSplitContainers(splitsToRemove);
// If there is any TaskFragment split with the PIP TaskFragment, update their presentations
// since the split is dismissed.
// We don't want to close any of them even if they are dependencies of the PIP TaskFragment.
@@ -1471,7 +1474,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
void removeContainers(@NonNull TaskContainer taskContainer,
@NonNull List<TaskFragmentContainer> containers) {
// Remove all split containers that included this one
- taskContainer.mContainers.removeAll(containers);
+ taskContainer.removeTaskFragmentContainers(containers);
// Marked as a pending removal which will be removed after it is actually removed on the
// server side (#onTaskFragmentVanished).
// In this way, we can keep track of the Task bounds until we no longer have any
@@ -1481,7 +1484,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Cleanup any split references.
final List<SplitContainer> containersToRemove = new ArrayList<>();
- for (SplitContainer splitContainer : taskContainer.mSplitContainers) {
+ final List<SplitContainer> splitContainers = taskContainer.getSplitContainers();
+ for (SplitContainer splitContainer : splitContainers) {
if (containersToRemove.contains(splitContainer)) {
// Don't need to check because it has been in the remove list.
continue;
@@ -1492,10 +1496,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
containersToRemove.add(splitContainer);
}
}
- taskContainer.mSplitContainers.removeAll(containersToRemove);
+ taskContainer.removeSplitContainers(containersToRemove);
// Cleanup any dependent references.
- for (TaskFragmentContainer containerToUpdate : taskContainer.mContainers) {
+ final List<TaskFragmentContainer> taskFragmentContainers =
+ taskContainer.getTaskFragmentContainers();
+ for (TaskFragmentContainer containerToUpdate : taskFragmentContainers) {
containerToUpdate.removeContainersToFinishOnExit(containers);
}
}
@@ -1534,8 +1540,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (taskContainer == null) {
return null;
}
- for (int i = taskContainer.mContainers.size() - 1; i >= 0; i--) {
- final TaskFragmentContainer container = taskContainer.mContainers.get(i);
+ final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
+ for (int i = containers.size() - 1; i >= 0; i--) {
+ final TaskFragmentContainer container = containers.get(i);
if (!container.isFinished() && (container.getRunningActivityCount() > 0
// We may be waiting for the top TaskFragment to become non-empty after
// creation. In that case, we don't want to treat the TaskFragment below it as
@@ -1629,7 +1636,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/** Whether the given split is the topmost split in the Task. */
private boolean isTopMostSplit(@NonNull SplitContainer splitContainer) {
final List<SplitContainer> splitContainers = splitContainer.getPrimaryContainer()
- .getTaskContainer().mSplitContainers;
+ .getTaskContainer().getSplitContainers();
return splitContainer == splitContainers.get(splitContainers.size() - 1);
}
@@ -1641,7 +1648,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (container == null) {
return null;
}
- final List<SplitContainer> splitContainers = container.getTaskContainer().mSplitContainers;
+ final List<SplitContainer> splitContainers =
+ container.getTaskContainer().getSplitContainers();
if (splitContainers.isEmpty()) {
return null;
}
@@ -1665,7 +1673,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@NonNull TaskFragmentContainer firstContainer,
@NonNull TaskFragmentContainer secondContainer) {
final List<SplitContainer> splitContainers = firstContainer.getTaskContainer()
- .mSplitContainers;
+ .getSplitContainers();
for (int i = splitContainers.size() - 1; i >= 0; i--) {
final SplitContainer splitContainer = splitContainers.get(i);
final TaskFragmentContainer primary = splitContainer.getPrimaryContainer();
@@ -1930,7 +1938,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
+ final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
+ .getTaskFragmentContainers();
for (TaskFragmentContainer container : containers) {
if (container.getTaskFragmentToken().equals(fragmentToken)) {
return container;
@@ -1945,7 +1954,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
SplitContainer getSplitContainer(@NonNull IBinder token) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<SplitContainer> containers = mTaskContainers.valueAt(i).mSplitContainers;
+ final List<SplitContainer> containers = mTaskContainers.valueAt(i).getSplitContainers();
for (SplitContainer container : containers) {
if (container.getToken().equals(token)) {
return container;
@@ -2091,7 +2100,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
- .mContainers;
+ .getTaskFragmentContainers();
for (int j = containers.size() - 1; j >= 0; j--) {
final TaskFragmentContainer container = containers.get(j);
if (!container.hasActivity(activityToken)
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 4b15bb187035..4580c9836168 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -51,11 +51,11 @@ class TaskContainer {
/** Active TaskFragments in this Task. */
@NonNull
- final List<TaskFragmentContainer> mContainers = new ArrayList<>();
+ private final List<TaskFragmentContainer> mContainers = new ArrayList<>();
/** Active split pairs in this Task. */
@NonNull
- final List<SplitContainer> mSplitContainers = new ArrayList<>();
+ private final List<SplitContainer> mSplitContainers = new ArrayList<>();
@NonNull
private final Configuration mConfiguration;
@@ -207,6 +207,53 @@ class TaskContainer {
return false;
}
+ /**
+ * Returns a list of {@link SplitContainer}. Do not modify the containers directly on the
+ * returned list. Use {@link #addSplitContainer} or {@link #removeSplitContainers} instead.
+ */
+ @NonNull
+ List<SplitContainer> getSplitContainers() {
+ return mSplitContainers;
+ }
+
+ void addSplitContainer(@NonNull SplitContainer splitContainer) {
+ mSplitContainers.add(splitContainer);
+ }
+
+ void removeSplitContainers(@NonNull List<SplitContainer> containers) {
+ mSplitContainers.removeAll(containers);
+ }
+
+ void addTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) {
+ mContainers.add(taskFragmentContainer);
+ }
+
+ void addTaskFragmentContainer(int index, @NonNull TaskFragmentContainer taskFragmentContainer) {
+ mContainers.add(index, taskFragmentContainer);
+ }
+
+ void removeTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) {
+ mContainers.remove(taskFragmentContainer);
+ }
+
+ void removeTaskFragmentContainers(@NonNull List<TaskFragmentContainer> taskFragmentContainer) {
+ mContainers.removeAll(taskFragmentContainer);
+ }
+
+ void clearTaskFragmentContainer() {
+ mContainers.clear();
+ }
+
+ /**
+ * Returns a list of {@link TaskFragmentContainer}. Do not modify the containers directly on
+ * the returned list. Use {@link #addTaskFragmentContainer},
+ * {@link #removeTaskFragmentContainer} or other related methods instead.
+ */
+ @NonNull
+ List<TaskFragmentContainer> getTaskFragmentContainers() {
+ return mContainers;
+ }
+
/** Adds the descriptors of split states in this Task to {@code outSplitStates}. */
void getSplitStates(@NonNull List<SplitInfo> outSplitStates) {
for (SplitContainer container : mSplitContainers) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 60be9d16d749..61df335515b8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -180,23 +180,25 @@ class TaskFragmentContainer {
throw new IllegalArgumentException(
"pairedPrimaryContainer must be in the same Task");
}
- final int primaryIndex = taskContainer.mContainers.indexOf(pairedPrimaryContainer);
- taskContainer.mContainers.add(primaryIndex + 1, this);
+ final int primaryIndex = taskContainer.indexOf(pairedPrimaryContainer);
+ taskContainer.addTaskFragmentContainer(primaryIndex + 1, this);
} else if (pendingAppearedActivity != null) {
// The TaskFragment will be positioned right above the pending appeared Activity. If any
// existing TaskFragment is empty with pending Intent, it is likely that the Activity of
// the pending Intent hasn't been created yet, so the new Activity should be below the
// empty TaskFragment.
- int i = taskContainer.mContainers.size() - 1;
+ final List<TaskFragmentContainer> containers =
+ taskContainer.getTaskFragmentContainers();
+ int i = containers.size() - 1;
for (; i >= 0; i--) {
- final TaskFragmentContainer container = taskContainer.mContainers.get(i);
+ final TaskFragmentContainer container = containers.get(i);
if (!container.isEmpty() || container.getPendingAppearedIntent() == null) {
break;
}
}
- taskContainer.mContainers.add(i + 1, this);
+ taskContainer.addTaskFragmentContainer(i + 1, this);
} else {
- taskContainer.mContainers.add(this);
+ taskContainer.addTaskFragmentContainer(this);
}
if (pendingAppearedActivity != null) {
addPendingAppearedActivity(pendingAppearedActivity);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index ff08782e8cd8..9e264726a65a 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -183,23 +183,23 @@ public class SplitControllerTest {
// tf2 has running activity so is active.
final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class);
doReturn(1).when(tf2).getRunningActivityCount();
- taskContainer.mContainers.add(tf2);
+ taskContainer.addTaskFragmentContainer(tf2);
// tf3 is finished so is not active.
final TaskFragmentContainer tf3 = mock(TaskFragmentContainer.class);
doReturn(true).when(tf3).isFinished();
doReturn(false).when(tf3).isWaitingActivityAppear();
- taskContainer.mContainers.add(tf3);
+ taskContainer.addTaskFragmentContainer(tf3);
mSplitController.mTaskContainers.put(TASK_ID, taskContainer);
assertWithMessage("Must return tf2 because tf3 is not active.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2);
- taskContainer.mContainers.remove(tf3);
+ taskContainer.removeTaskFragmentContainer(tf3);
assertWithMessage("Must return tf2 because tf2 has running activity.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2);
- taskContainer.mContainers.remove(tf2);
+ taskContainer.removeTaskFragmentContainer(tf2);
assertWithMessage("Must return tf because we are waiting for tf1 to appear.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf1);
@@ -320,11 +320,11 @@ public class SplitControllerTest {
doReturn(tf).when(splitContainer).getSecondaryContainer();
doReturn(createTestTaskContainer()).when(splitContainer).getTaskContainer();
doReturn(createSplitRule(mActivity, mActivity)).when(splitContainer).getSplitRule();
- final List<SplitContainer> splitContainers =
- mSplitController.getTaskContainer(TASK_ID).mSplitContainers;
- splitContainers.add(splitContainer);
+ final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
+ taskContainer.addSplitContainer(splitContainer);
// Add a mock SplitContainer on top of splitContainer
- splitContainers.add(1, mock(SplitContainer.class));
+ final SplitContainer splitContainer2 = mock(SplitContainer.class);
+ taskContainer.addSplitContainer(splitContainer2);
mSplitController.updateContainer(mTransaction, tf);
@@ -332,7 +332,9 @@ public class SplitControllerTest {
// Verify if one or both containers in the top SplitContainer are finished,
// dismissPlaceholder() won't be called.
- splitContainers.remove(1);
+ final ArrayList<SplitContainer> splitContainersToRemove = new ArrayList<>();
+ splitContainersToRemove.add(splitContainer2);
+ taskContainer.removeSplitContainers(splitContainersToRemove);
doReturn(true).when(tf).isFinished();
mSplitController.updateContainer(mTransaction, tf);
@@ -363,7 +365,8 @@ public class SplitControllerTest {
final Activity r1 = createMockActivity();
addSplitTaskFragments(r0, r1);
final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
- final TaskFragmentContainer taskFragmentContainer = taskContainer.mContainers.get(0);
+ final TaskFragmentContainer taskFragmentContainer =
+ taskContainer.getTaskFragmentContainers().get(0);
spyOn(taskContainer);
// No update when the Task is invisible.
@@ -377,7 +380,7 @@ public class SplitControllerTest {
doReturn(true).when(taskContainer).isVisible();
mSplitController.updateContainer(mTransaction, taskFragmentContainer);
- verify(mSplitPresenter).updateSplitContainer(taskContainer.mSplitContainers.get(0),
+ verify(mSplitPresenter).updateSplitContainer(taskContainer.getSplitContainers().get(0),
mTransaction);
}
@@ -1090,8 +1093,8 @@ public class SplitControllerTest {
verify(mTransaction).finishActivity(mActivity.getActivityToken());
verify(mTransaction).finishActivity(secondaryActivity0.getActivityToken());
verify(mTransaction).finishActivity(secondaryActivity1.getActivityToken());
- assertTrue(taskContainer.mContainers.isEmpty());
- assertTrue(taskContainer.mSplitContainers.isEmpty());
+ assertTrue(taskContainer.getTaskFragmentContainers().isEmpty());
+ assertTrue(taskContainer.getSplitContainers().isEmpty());
}
@Test
@@ -1363,15 +1366,13 @@ public class SplitControllerTest {
TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
tf.setInfo(mTransaction, createMockTaskFragmentInfo(tf, mActivity));
- List<TaskFragmentContainer> containers = mSplitController.mTaskContainers.get(TASK_ID)
- .mContainers;
-
- assertEquals(containers.get(0), tf);
+ final TaskContainer taskContainer = mSplitController.mTaskContainers.get(TASK_ID);
+ assertEquals(taskContainer.getTaskFragmentContainers().get(0), tf);
mSplitController.finishActivityStacks(Collections.singleton(tf.getTaskFragmentToken()));
verify(mSplitPresenter).deleteTaskFragment(any(), eq(tf.getTaskFragmentToken()));
- assertTrue(containers.isEmpty());
+ assertTrue(taskContainer.getTaskFragmentContainers().isEmpty());
}
@Test
@@ -1381,10 +1382,8 @@ public class SplitControllerTest {
bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity));
topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
- List<TaskFragmentContainer> containers = mSplitController.mTaskContainers.get(TASK_ID)
- .mContainers;
-
- assertEquals(containers.size(), 2);
+ final TaskContainer taskContainer = mSplitController.mTaskContainers.get(TASK_ID);
+ assertEquals(taskContainer.getTaskFragmentContainers().size(), 2);
Set<IBinder> activityStackTokens = new ArraySet<>(new IBinder[]{
topTf.getTaskFragmentToken(), bottomTf.getTaskFragmentToken()});
@@ -1403,7 +1402,7 @@ public class SplitControllerTest {
+ "regardless of the order in ActivityStack set",
topTf.getTaskFragmentToken(), fragmentTokens.get(1));
- assertTrue(containers.isEmpty());
+ assertTrue(taskContainer.getTaskFragmentContainers().isEmpty());
}
@Test
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 13e709271221..11af1d1f20e1 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -127,7 +127,7 @@ public class TaskContainerTest {
assertFalse(taskContainer.isEmpty());
taskContainer.mFinishedContainer.add(tf.getTaskFragmentToken());
- taskContainer.mContainers.clear();
+ taskContainer.clearTaskFragmentContainer();
assertFalse(taskContainer.isEmpty());
}
@@ -152,13 +152,13 @@ public class TaskContainerTest {
assertNull(taskContainer.getTopNonFinishingActivity());
final TaskFragmentContainer tf0 = mock(TaskFragmentContainer.class);
- taskContainer.mContainers.add(tf0);
+ taskContainer.addTaskFragmentContainer(tf0);
final Activity activity0 = mock(Activity.class);
doReturn(activity0).when(tf0).getTopNonFinishingActivity();
assertEquals(activity0, taskContainer.getTopNonFinishingActivity());
final TaskFragmentContainer tf1 = mock(TaskFragmentContainer.class);
- taskContainer.mContainers.add(tf1);
+ taskContainer.addTaskFragmentContainer(tf1);
assertEquals(activity0, taskContainer.getTopNonFinishingActivity());
final Activity activity1 = mock(Activity.class);
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 54978bd4496d..71598938f42f 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -42,16 +42,19 @@ filegroup {
filegroup {
name: "wm_shell_util-sources",
srcs: [
- "src/com/android/wm/shell/util/**/*.java",
+ "src/com/android/wm/shell/animation/Interpolators.java",
+ "src/com/android/wm/shell/animation/PhysicsAnimator.kt",
+ "src/com/android/wm/shell/common/bubbles/*.kt",
+ "src/com/android/wm/shell/common/bubbles/*.java",
+ "src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt",
"src/com/android/wm/shell/common/split/SplitScreenConstants.java",
- "src/com/android/wm/shell/sysui/ShellSharedConstants.java",
"src/com/android/wm/shell/common/TransactionPool.java",
- "src/com/android/wm/shell/common/bubbles/*.java",
"src/com/android/wm/shell/common/TriangleShape.java",
- "src/com/android/wm/shell/animation/Interpolators.java",
+ "src/com/android/wm/shell/draganddrop/DragAndDropConstants.java",
"src/com/android/wm/shell/pip/PipContentOverlay.java",
"src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java",
- "src/com/android/wm/shell/draganddrop/DragAndDropConstants.java",
+ "src/com/android/wm/shell/sysui/ShellSharedConstants.java",
+ "src/com/android/wm/shell/util/**/*.java",
],
path: "src",
}
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_section.xml b/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_section.xml
new file mode 100644
index 000000000000..d99d64d8da20
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_section.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <ripple android:color="#99999999">
+ <item android:drawable="@drawable/bubble_manage_menu_bg" />
+ </ripple>
+ </item>
+ <item android:drawable="@drawable/bubble_manage_menu_bg" />
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
index 5d7771366bec..ce242751c172 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml
@@ -13,13 +13,20 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <group android:translateY="8.0">
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="128dp"
+ android:height="4dp"
+ android:viewportWidth="128"
+ android:viewportHeight="4"
+ >
+ <group>
+ <clip-path
+ android:pathData="M2 0H126C127.105 0 128 0.895431 128 2C128 3.10457 127.105 4 126 4H2C0.895431 4 0 3.10457 0 2C0 0.895431 0.895431 0 2 0Z"
+ />
<path
- android:fillColor="@android:color/black" android:pathData="M3,5V3H21V5Z"/>
+ android:pathData="M0 0V4H128V0"
+ android:fillColor="@android:color/black"
+ />
</group>
</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/ic_expand_less.xml b/libs/WindowManager/Shell/res/drawable/ic_expand_less.xml
new file mode 100644
index 000000000000..f4508464883d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/ic_expand_less.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M18.59,16.41L20,15L12,7L4,15L5.41,16.41L12,9.83"
+ android:fillColor="#5F6368"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml
new file mode 100644
index 000000000000..ddcd5c60d9c8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<com.android.wm.shell.bubbles.bar.BubbleBarMenuItemView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/bubble_bar_manage_menu_item_height"
+ android:gravity="center_vertical"
+ android:paddingStart="@dimen/bubble_menu_padding"
+ android:paddingEnd="@dimen/bubble_menu_padding"
+ android:background="@drawable/bubble_manage_menu_row">
+
+ <ImageView
+ android:id="@+id/bubble_bar_menu_item_icon"
+ android:layout_width="@dimen/bubble_bar_manage_menu_item_icon_size"
+ android:layout_height="@dimen/bubble_bar_manage_menu_item_icon_size"
+ android:contentDescription="@null"/>
+
+ <TextView
+ android:id="@+id/bubble_bar_menu_item_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
+
+</com.android.wm.shell.bubbles.bar.BubbleBarMenuItemView> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
new file mode 100644
index 000000000000..82e5aee41ff2
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<com.android.wm.shell.bubbles.bar.BubbleBarMenuView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:minWidth="@dimen/bubble_bar_manage_menu_min_width"
+ android:orientation="vertical"
+ android:elevation="@dimen/bubble_manage_menu_elevation"
+ android:paddingTop="@dimen/bubble_bar_manage_menu_padding_top"
+ android:paddingHorizontal="@dimen/bubble_bar_manage_menu_padding"
+ android:paddingBottom="@dimen/bubble_bar_manage_menu_padding"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/bubble_bar_manage_menu_bubble_section"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/bubble_bar_manage_menu_item_height"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:paddingStart="14dp"
+ android:paddingEnd="12dp"
+ android:background="@drawable/bubble_manage_menu_section"
+ android:elevation="@dimen/bubble_manage_menu_elevation">
+
+ <ImageView
+ android:id="@+id/bubble_bar_manage_menu_bubble_icon"
+ android:layout_width="@dimen/bubble_menu_icon_size"
+ android:layout_height="@dimen/bubble_menu_icon_size"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:id="@+id/bubble_bar_manage_menu_bubble_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_weight="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault" />
+
+ <ImageView
+ android:id="@+id/bubble_bar_manage_menu_dismiss_icon"
+ android:layout_width="@dimen/bubble_bar_manage_menu_dismiss_icon_size"
+ android:layout_height="@dimen/bubble_bar_manage_menu_dismiss_icon_size"
+ android:layout_marginStart="8dp"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_expand_less"
+ app:tint="?android:attr/textColorPrimary" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/bubble_bar_manage_menu_actions_section"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginTop="@dimen/bubble_bar_manage_menu_section_spacing"
+ android:background="@drawable/bubble_manage_menu_bg"
+ android:elevation="@dimen/bubble_manage_menu_elevation" />
+
+</com.android.wm.shell.bubbles.bar.BubbleBarMenuView> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index 1d6864c152c2..0ca912e20527 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -28,6 +28,7 @@
android:layout_width="176dp"
android:layout_height="42dp"
android:paddingHorizontal="24dp"
+ android:paddingVertical="19dp"
android:contentDescription="@string/handle_text"
android:src="@drawable/decor_handle_dark"
tools:tint="@color/desktop_mode_caption_handle_bar_dark"
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 171a6b2fe5fb..f2a07857cd4a 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -30,6 +30,9 @@
<color name="bubbles_light">#FFFFFF</color>
<color name="bubbles_dark">@color/GM2_grey_800</color>
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
+ <color name="bubble_bar_expanded_view_handle_light">#EBffffff</color>
+ <color name="bubble_bar_expanded_view_handle_dark">#99000000</color>
+ <color name="bubble_bar_expanded_view_menu_close">#DC362E</color>
<!-- PiP -->
<color name="pip_custom_close_bg">#D93025</color>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 2be34c90a661..14e82539a6a5 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -229,7 +229,25 @@
<!-- Size of the bubble bar (height), should match transient_taskbar_size in Launcher. -->
<dimen name="bubblebar_size">72dp</dimen>
<!-- The size of the drag handle / menu shown along with a bubble bar expanded view. -->
- <dimen name="bubblebar_expanded_view_menu_size">16dp</dimen>
+ <dimen name="bubble_bar_expanded_view_handle_size">40dp</dimen>
+ <!-- The width of the drag handle shown along with a bubble bar expanded view. -->
+ <dimen name="bubble_bar_expanded_view_handle_width">128dp</dimen>
+ <!-- The height of the drag handle shown along with a bubble bar expanded view. -->
+ <dimen name="bubble_bar_expanded_view_handle_height">4dp</dimen>
+ <!-- Minimum width of the bubble bar manage menu. -->
+ <dimen name="bubble_bar_manage_menu_min_width">200dp</dimen>
+ <!-- Size of the dismiss icon in the bubble bar manage menu. -->
+ <dimen name="bubble_bar_manage_menu_dismiss_icon_size">16dp</dimen>
+ <!-- Padding of the bubble bar manage menu, provides space for menu shadows -->
+ <dimen name="bubble_bar_manage_menu_padding">8dp</dimen>
+ <!-- Top padding of the bubble bar manage menu -->
+ <dimen name="bubble_bar_manage_menu_padding_top">2dp</dimen>
+ <!-- Spacing between sections of the bubble bar manage menu -->
+ <dimen name="bubble_bar_manage_menu_section_spacing">2dp</dimen>
+ <!-- Height of an item in the bubble bar manage menu. -->
+ <dimen name="bubble_bar_manage_menu_item_height">52dp</dimen>
+ <!-- Size of the icons in the bubble bar manage menu. -->
+ <dimen name="bubble_bar_manage_menu_item_icon_size">20dp</dimen>
<!-- Bottom and end margin for compat buttons. -->
<dimen name="compat_button_margin">24dp</dimen>
@@ -398,7 +416,11 @@
<!-- The radius of the caption menu shadow. -->
<dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
- <dimen name="freeform_resize_handle">30dp</dimen>
+ <dimen name="freeform_resize_handle">15dp</dimen>
<dimen name="freeform_resize_corner">44dp</dimen>
+
+ <!-- The height of the area at the top of the screen where a freeform task will transition to
+ fullscreen if dragged until the top bound of the task is within the area. -->
+ <dimen name="desktop_mode_transition_area_height">16dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 102f2cb4b8d0..7e09c989e1b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -422,6 +422,7 @@ public class Bubble implements BubbleViewProvider {
}
if (mBubbleBarExpandedView != null) {
mBubbleBarExpandedView.cleanUpExpandedState();
+ mBubbleBarExpandedView = null;
}
if (mIntent != null) {
mIntent.unregisterCancelListener(mIntentCancelListener);
@@ -549,10 +550,10 @@ public class Bubble implements BubbleViewProvider {
/**
* Set visibility of bubble in the expanded state.
*
- * @param visibility {@code true} if the expanded bubble should be visible on the screen.
- *
- * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
+ * <p>Note that this contents visibility doesn't affect visibility at {@link android.view.View},
* and setting {@code false} actually means rendering the expanded view in transparent.
+ *
+ * @param visibility {@code true} if the expanded bubble should be visible on the screen.
*/
@Override
public void setTaskViewVisibility(boolean visibility) {
@@ -855,7 +856,8 @@ public class Bubble implements BubbleViewProvider {
return mIsAppBubble;
}
- Intent getSettingsIntent(final Context context) {
+ /** Creates open app settings intent */
+ public Intent getSettingsIntent(final Context context) {
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
final int uid = getUid(context);
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 0fdfbb8c0c61..688023737074 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
@@ -64,7 +64,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.notification.NotificationListenerService;
@@ -92,6 +91,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.bubbles.properties.BubbleProperties;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -143,16 +143,6 @@ public class BubbleController implements ConfigurationChangeListener,
private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";
- // TODO(b/256873975) Should use proper flag when available to shell/launcher
- /**
- * Whether bubbles are showing in the bubble bar from launcher. This is only available
- * on large screens and {@link BubbleController#isShowingAsBubbleBar()} should be used
- * to check all conditions that indicate if the bubble bar is in use.
- */
- private static final boolean BUBBLE_BAR_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
-
-
/**
* Common interface to send updates to bubble views.
*/
@@ -195,6 +185,7 @@ public class BubbleController implements ConfigurationChangeListener,
private final ShellController mShellController;
private final ShellCommandHandler mShellCommandHandler;
private final IWindowManager mWmService;
+ private final BubbleProperties mBubbleProperties;
// Used to post to main UI thread
private final ShellExecutor mMainExecutor;
@@ -291,7 +282,8 @@ public class BubbleController implements ConfigurationChangeListener,
@ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue,
- IWindowManager wmService) {
+ IWindowManager wmService,
+ BubbleProperties bubbleProperties) {
mContext = context;
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
@@ -328,6 +320,7 @@ public class BubbleController implements ConfigurationChangeListener,
mDragAndDropController = dragAndDropController;
mSyncQueue = syncQueue;
mWmService = wmService;
+ mBubbleProperties = bubbleProperties;
shellInit.addInitCallback(this::onInit, this);
}
@@ -518,11 +511,14 @@ public class BubbleController implements ConfigurationChangeListener,
/**
* Sets a listener to be notified of bubble updates. This is used by launcher so that
* it may render bubbles in itself. Only one listener is supported.
+ *
+ * <p>If bubble bar is supported, bubble views will be updated to switch to bar mode.
*/
public void registerBubbleStateListener(Bubbles.BubbleStateListener listener) {
- if (isShowingAsBubbleBar()) {
- // Only set the listener if bubble bar is showing.
+ if (canShowAsBubbleBar() && listener != null) {
+ // Only set the listener if we can show the bubble bar.
mBubbleStateListener = listener;
+ setUpBubbleViewsForMode();
sendInitialListenerUpdate();
} else {
mBubbleStateListener = null;
@@ -531,9 +527,15 @@ public class BubbleController implements ConfigurationChangeListener,
/**
* Unregisters the {@link Bubbles.BubbleStateListener}.
+ *
+ * <p>If there's an existing listener, then we're switching back to stack mode and bubble views
+ * will be updated accordingly.
*/
public void unregisterBubbleStateListener() {
- mBubbleStateListener = null;
+ if (mBubbleStateListener != null) {
+ mBubbleStateListener = null;
+ setUpBubbleViewsForMode();
+ }
}
/**
@@ -645,8 +647,12 @@ public class BubbleController implements ConfigurationChangeListener,
/** Whether bubbles are showing in the bubble bar. */
public boolean isShowingAsBubbleBar() {
- // TODO(b/269670598): should also check that we're in gesture nav
- return BUBBLE_BAR_ENABLED && mBubblePositioner.isLargeScreen();
+ return canShowAsBubbleBar() && mBubbleStateListener != null;
+ }
+
+ /** Whether the current configuration supports showing as bubble bar. */
+ private boolean canShowAsBubbleBar() {
+ return mBubbleProperties.isBubbleBarEnabled() && mBubblePositioner.isLargeScreen();
}
/** Whether this userId belongs to the current user. */
@@ -719,6 +725,7 @@ public class BubbleController implements ConfigurationChangeListener,
// TODO(b/273312602): consider foldables where we do need a stack view when folded
if (mLayerView == null) {
mLayerView = new BubbleBarLayerView(mContext, this);
+ mLayerView.setUnBubbleConversationCallback(mSysuiProxy::onUnbubbleConversation);
}
} else {
if (mStackView == null) {
@@ -773,12 +780,12 @@ public class BubbleController implements ConfigurationChangeListener,
try {
mAddedToWindowManager = true;
registerBroadcastReceiver();
- mBubbleData.getOverflow().initialize(this);
+ mBubbleData.getOverflow().initialize(this, isShowingAsBubbleBar());
// (TODO: b/273314541) some duplication in the inset listener
if (isShowingAsBubbleBar()) {
mWindowManager.addView(mLayerView, mWmLayoutParams);
mLayerView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
- if (!windowInsets.equals(mWindowInsets)) {
+ if (!windowInsets.equals(mWindowInsets) && mLayerView != null) {
mWindowInsets = windowInsets;
mBubblePositioner.update();
mLayerView.onDisplaySizeChanged();
@@ -788,7 +795,7 @@ public class BubbleController implements ConfigurationChangeListener,
} else {
mWindowManager.addView(mStackView, mWmLayoutParams);
mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
- if (!windowInsets.equals(mWindowInsets)) {
+ if (!windowInsets.equals(mWindowInsets) && mStackView != null) {
mWindowInsets = windowInsets;
mBubblePositioner.update();
mStackView.onDisplaySizeChanged();
@@ -1065,9 +1072,18 @@ public class BubbleController implements ConfigurationChangeListener,
* Expands and selects the provided bubble as long as it already exists in the stack or the
* overflow.
*
- * This is used by external callers (launcher).
+ * <p>This is used by external callers (launcher).
*/
- public void expandStackAndSelectBubbleFromLauncher(String key) {
+ @VisibleForTesting
+ public void expandStackAndSelectBubbleFromLauncher(String key, boolean onLauncherHome) {
+ mBubblePositioner.setShowingInBubbleBar(onLauncherHome);
+
+ if (BubbleOverflow.KEY.equals(key)) {
+ mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow());
+ mLayerView.showExpandedView(mBubbleData.getOverflow());
+ return;
+ }
+
Bubble b = mBubbleData.getAnyBubbleWithkey(key);
if (b == null) {
return;
@@ -1220,6 +1236,13 @@ public class BubbleController implements ConfigurationChangeListener,
}
/**
+ * Dismiss bubble if it exists and remove it from the stack
+ */
+ public void dismissBubble(Bubble bubble, @Bubbles.DismissReason int reason) {
+ mBubbleData.dismissBubbleWithKey(bubble.getKey(), reason);
+ }
+
+ /**
* Performs a screenshot that may exclude the bubble layer, if one is present. The screenshot
* can be access via the supplied {@link SynchronousScreenCaptureListener#getBuffer()}
* asynchronously.
@@ -1259,7 +1282,9 @@ public class BubbleController implements ConfigurationChangeListener,
return;
}
mOverflowDataLoadNeeded = false;
- mDataRepository.loadBubbles(mCurrentUserId, (bubbles) -> {
+ List<UserInfo> users = mUserManager.getAliveUsers();
+ List<Integer> userIds = users.stream().map(userInfo -> userInfo.id).toList();
+ mDataRepository.loadBubbles(mCurrentUserId, userIds, (bubbles) -> {
bubbles.forEach(bubble -> {
if (mBubbleData.hasAnyBubbleWithKey(bubble.getKey())) {
// if the bubble is already active, there's no need to push it to overflow
@@ -1278,6 +1303,50 @@ public class BubbleController implements ConfigurationChangeListener,
});
}
+ void setUpBubbleViewsForMode() {
+ mBubbleViewCallback = isShowingAsBubbleBar()
+ ? mBubbleBarViewCallback
+ : mBubbleStackViewCallback;
+
+ // reset the overflow so that it can be re-added later if needed.
+ if (mStackView != null) {
+ mStackView.resetOverflowView();
+ mStackView.removeAllViews();
+ }
+ // cleanup existing bubble views so they can be recreated later if needed.
+ mBubbleData.getBubbles().forEach(Bubble::cleanupViews);
+
+ // remove the current bubble container from window manager, null it out, and create a new
+ // container based on the current mode.
+ removeFromWindowManagerMaybe();
+ mLayerView = null;
+ mStackView = null;
+ ensureBubbleViewsAndWindowCreated();
+
+ // inflate bubble views
+ BubbleViewInfoTask.Callback callback = null;
+ if (!isShowingAsBubbleBar()) {
+ callback = b -> {
+ if (mStackView != null) {
+ mStackView.addBubble(b);
+ mStackView.setSelectedBubble(b);
+ } else {
+ Log.w(TAG, "Tried to add a bubble to the stack but the stack is null");
+ }
+ };
+ }
+ for (int i = mBubbleData.getBubbles().size() - 1; i >= 0; i--) {
+ Bubble bubble = mBubbleData.getBubbles().get(i);
+ bubble.inflate(callback,
+ mContext,
+ this,
+ mStackView,
+ mLayerView,
+ mBubbleIconFactory,
+ false /* skipInflation */);
+ }
+ }
+
/**
* Adds or updates a bubble associated with the provided notification entry.
*
@@ -1738,7 +1807,7 @@ public class BubbleController implements ConfigurationChangeListener,
// Update the cached state for queries from SysUI
mImpl.mCachedState.update(update);
- if (isShowingAsBubbleBar() && mBubbleStateListener != null) {
+ if (isShowingAsBubbleBar()) {
BubbleBarUpdate bubbleBarUpdate = update.toBubbleBarUpdate();
// Some updates aren't relevant to the bubble bar so check first.
if (bubbleBarUpdate.anythingChanged()) {
@@ -1846,7 +1915,7 @@ public class BubbleController implements ConfigurationChangeListener,
if (mStackView != null) {
mStackView.setVisibility(VISIBLE);
}
- if (mLayerView != null && isStackExpanded()) {
+ if (mLayerView != null) {
mLayerView.setVisibility(VISIBLE);
}
}
@@ -1860,10 +1929,17 @@ public class BubbleController implements ConfigurationChangeListener,
}
@VisibleForTesting
+ @Nullable
public BubbleStackView getStackView() {
return mStackView;
}
+ @VisibleForTesting
+ @Nullable
+ public BubbleBarLayerView getLayerView() {
+ return mLayerView;
+ }
+
/**
* Check if notification panel is in an expanded state.
* Makes a call to System UI process and delivers the result via {@code callback} on the
@@ -2002,22 +2078,18 @@ public class BubbleController implements ConfigurationChangeListener,
@Override
public void registerBubbleListener(IBubblesListener listener) {
- mMainExecutor.execute(() -> {
- mListener.register(listener);
- });
+ mMainExecutor.execute(() -> mListener.register(listener));
}
@Override
public void unregisterBubbleListener(IBubblesListener listener) {
- mMainExecutor.execute(() -> mListener.unregister());
+ mMainExecutor.execute(mListener::unregister);
}
@Override
public void showBubble(String key, boolean onLauncherHome) {
- mMainExecutor.execute(() -> {
- mBubblePositioner.setShowingInBubbleBar(onLauncherHome);
- mController.expandStackAndSelectBubbleFromLauncher(key);
- });
+ mMainExecutor.execute(
+ () -> mController.expandStackAndSelectBubbleFromLauncher(key, onLauncherHome));
}
@Override
@@ -2029,11 +2101,6 @@ public class BubbleController implements ConfigurationChangeListener,
public void collapseBubbles() {
mMainExecutor.execute(() -> mController.collapseStack());
}
-
- @Override
- public void onTaskbarStateChanged(int newState) {
- // TODO (b/269670598)
- }
}
private class BubblesImpl implements Bubbles {
@@ -2144,7 +2211,7 @@ public class BubbleController implements ConfigurationChangeListener,
pw.println(" suppressing: " + key);
}
- pw.print("mAppBubbleTaskIds: " + mAppBubbleTaskIds.values());
+ pw.println("mAppBubbleTaskIds: " + mAppBubbleTaskIds.values());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index e37c785f15f5..896a33449185 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -17,7 +17,6 @@ package com.android.wm.shell.bubbles
import android.annotation.SuppressLint
import android.annotation.UserIdInt
-import android.content.Context
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
@@ -25,6 +24,8 @@ import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LA
import android.content.pm.UserInfo
import android.os.UserHandle
import android.util.Log
+import android.util.SparseArray
+import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.bubbles.Bubbles.BubbleMetadataFlagListener
import com.android.wm.shell.bubbles.storage.BubbleEntity
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
@@ -33,19 +34,19 @@ import com.android.wm.shell.common.ShellExecutor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
-internal class BubbleDataRepository(
- context: Context,
+class BubbleDataRepository(
private val launcherApps: LauncherApps,
- private val mainExecutor: ShellExecutor
+ private val mainExecutor: ShellExecutor,
+ private val persistentRepository: BubblePersistentRepository,
) {
private val volatileRepository = BubbleVolatileRepository(launcherApps)
- private val persistentRepository = BubblePersistentRepository(context)
- private val ioScope = CoroutineScope(Dispatchers.IO)
+ private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private var job: Job? = null
// For use in Bubble construction.
@@ -98,6 +99,43 @@ internal class BubbleDataRepository(
if (volatileRepository.sanitizeBubbles(userIds)) persistToDisk()
}
+ /**
+ * Removes all entities that don't have a user in the activeUsers list, if any entities were
+ * removed it persists the new list to disk.
+ */
+ private fun filterForActiveUsersAndPersist(
+ activeUsers: List<Int>,
+ entitiesByUser: SparseArray<List<BubbleEntity>>
+ ): SparseArray<List<BubbleEntity>> {
+ val validEntitiesByUser = SparseArray<List<BubbleEntity>>()
+ var entitiesChanged = false
+ for (i in 0 until entitiesByUser.size()) {
+ val parentUserId = entitiesByUser.keyAt(i)
+ if (activeUsers.contains(parentUserId)) {
+ val validEntities = mutableListOf<BubbleEntity>()
+ // Check if each of the bubbles in the top-level user still has a valid user
+ // as it could belong to a profile and have a different id from the parent.
+ for (entity in entitiesByUser.get(parentUserId)) {
+ if (activeUsers.contains(entity.userId)) {
+ validEntities.add(entity)
+ } else {
+ entitiesChanged = true
+ }
+ }
+ if (validEntities.isNotEmpty()) {
+ validEntitiesByUser.put(parentUserId, validEntities)
+ }
+ } else {
+ entitiesChanged = true
+ }
+ }
+ if (entitiesChanged) {
+ persistToDisk(validEntitiesByUser)
+ return validEntitiesByUser
+ }
+ return entitiesByUser
+ }
+
private fun transform(bubbles: List<Bubble>): List<BubbleEntity> {
return bubbles.mapNotNull { b ->
BubbleEntity(
@@ -129,15 +167,17 @@ internal class BubbleDataRepository(
* Job C resumes and reaches yield() and is then cancelled
* Job D resumes and performs another blocking I/O
*/
- private fun persistToDisk() {
+ private fun persistToDisk(
+ entitiesByUser: SparseArray<List<BubbleEntity>> = volatileRepository.bubbles
+ ) {
val prev = job
- job = ioScope.launch {
+ job = coroutineScope.launch {
// if there was an ongoing disk I/O operation, they can be cancelled
prev?.cancelAndJoin()
// check for cancellation before disk I/O
yield()
// save to disk
- persistentRepository.persistsToDisk(volatileRepository.bubbles)
+ persistentRepository.persistsToDisk(entitiesByUser)
}
}
@@ -148,7 +188,12 @@ internal class BubbleDataRepository(
* bubbles.
*/
@SuppressLint("WrongConstant")
- fun loadBubbles(userId: Int, cb: (List<Bubble>) -> Unit) = ioScope.launch {
+ @VisibleForTesting
+ fun loadBubbles(
+ userId: Int,
+ currentUsers: List<Int>,
+ cb: (List<Bubble>) -> Unit
+ ) = coroutineScope.launch {
/**
* Load BubbleEntity from disk.
* e.g.
@@ -159,7 +204,12 @@ internal class BubbleDataRepository(
* ]
*/
val entitiesByUser = persistentRepository.readFromDisk()
- val entities = entitiesByUser.get(userId) ?: return@launch
+
+ // Before doing anything, validate that the entities we loaded are valid & have an existing
+ // user.
+ val validEntitiesByUser = filterForActiveUsersAndPersist(currentUsers, entitiesByUser)
+
+ val entities = validEntitiesByUser.get(userId) ?: return@launch
volatileRepository.addBubbles(userId, entities)
/**
* Extract userId/packageName from these entities.
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 e1a3f3a1ac5d..67185655f2d2 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
@@ -948,9 +948,9 @@ public class BubbleExpandedView extends LinearLayout {
mTaskView.onLocationChanged();
}
if (mIsOverflow) {
- post(() -> {
- mOverflowView.show();
- });
+ // post this to the looper so that the view has a chance to be laid out before it can
+ // calculate row and column sizes correctly.
+ post(() -> mOverflowView.show());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index df7f5b4f0150..df19757203eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -44,6 +44,7 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl
private val inflater: LayoutInflater = LayoutInflater.from(context)
private var expandedView: BubbleExpandedView?
+ private var bubbleBarExpandedView: BubbleBarExpandedView? = null
private var overflowBtn: BadgedImageView?
init {
@@ -53,19 +54,26 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl
}
/** Call before use and again if cleanUpExpandedState was called. */
- fun initialize(controller: BubbleController) {
- createExpandedView()
- getExpandedView()?.initialize(controller, controller.stackView, true /* isOverflow */)
+ fun initialize(controller: BubbleController, forBubbleBar: Boolean) {
+ if (forBubbleBar) {
+ createBubbleBarExpandedView().initialize(controller, true /* isOverflow */)
+ } else {
+ createExpandedView()
+ .initialize(controller, controller.stackView, true /* isOverflow */)
+ }
}
fun cleanUpExpandedState() {
expandedView?.cleanUpExpandedState()
expandedView = null
+ bubbleBarExpandedView?.cleanUpExpandedState()
+ bubbleBarExpandedView = null
}
fun update() {
updateResources()
getExpandedView()?.applyThemeAttrs()
+ getBubbleBarExpandedView()?.applyThemeAttrs()
// Apply inset and new style to fresh icon drawable.
getIconView()?.setIconImageResource(R.drawable.bubble_ic_overflow_button)
updateBtnTheme()
@@ -151,26 +159,39 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl
overflowBtn?.updateDotVisibility(true /* animate */)
}
- fun createExpandedView(): BubbleExpandedView? {
- expandedView =
+ /** Creates the expanded view for bubbles showing in the stack view. */
+ private fun createExpandedView(): BubbleExpandedView {
+ val view =
inflater.inflate(
R.layout.bubble_expanded_view,
null /* root */,
false /* attachToRoot */
) as BubbleExpandedView
- expandedView?.applyThemeAttrs()
+ view.applyThemeAttrs()
+ expandedView = view
updateResources()
- return expandedView
+ return view
}
override fun getExpandedView(): BubbleExpandedView? {
return expandedView
}
- override fun getBubbleBarExpandedView(): BubbleBarExpandedView? {
- return null
+ /** Creates the expanded view for bubbles showing in the bubble bar. */
+ private fun createBubbleBarExpandedView(): BubbleBarExpandedView {
+ val view =
+ inflater.inflate(
+ R.layout.bubble_bar_expanded_view,
+ null, /* root */
+ false /* attachToRoot*/
+ ) as BubbleBarExpandedView
+ view.applyThemeAttrs()
+ bubbleBarExpandedView = view
+ return view
}
+ override fun getBubbleBarExpandedView(): BubbleBarExpandedView? = bubbleBarExpandedView
+
override fun getDotColor(): Int {
return dotColor
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index d101b0c4d7e8..cb08f93a9efa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -732,17 +732,25 @@ public class BubblePositioner {
/**
* How wide the expanded view should be when showing from the bubble bar.
*/
- public int getExpandedViewWidthForBubbleBar() {
- return mExpandedViewLargeScreenWidth;
+ public int getExpandedViewWidthForBubbleBar(boolean isOverflow) {
+ return isOverflow ? mOverflowWidth : mExpandedViewLargeScreenWidth;
}
/**
* How tall the expanded view should be when showing from the bubble bar.
*/
- public int getExpandedViewHeightForBubbleBar() {
+ public int getExpandedViewHeightForBubbleBar(boolean isOverflow) {
+ return isOverflow
+ ? mOverflowHeight
+ : getExpandedViewBottomForBubbleBar() - mInsets.top - mExpandedViewPadding;
+ }
+
+ /** The bottom position of the expanded view when showing above the bubble bar. */
+ public int getExpandedViewBottomForBubbleBar() {
return getAvailableRect().height()
+ + mInsets.top
- mBubbleBarSize
- - mExpandedViewPadding * 2
+ - mExpandedViewPadding
- getBubbleBarHomeAdjustment();
}
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 68fea41e134e..282db9e18d81 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
@@ -88,6 +88,8 @@ import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.common.bubbles.RelativeTouchListener;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import java.io.PrintWriter;
@@ -307,6 +309,7 @@ public class BubbleStackView extends FrameLayout
String bubblesOnScreen = BubbleDebugConfig.formatBubblesString(
getBubblesOnScreen(), getExpandedBubble());
+ pw.print(" stack visibility : "); pw.println(getVisibility());
pw.print(" bubbles on screen: "); pw.println(bubblesOnScreen);
pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress);
pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing());
@@ -968,6 +971,8 @@ public class BubbleStackView extends FrameLayout
mBubbleContainer.bringToFront();
mBubbleOverflow = mBubbleData.getOverflow();
+
+ resetOverflowView();
mBubbleContainer.addView(mBubbleOverflow.getIconView(),
mBubbleContainer.getChildCount() /* index */,
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
@@ -1179,6 +1184,7 @@ public class BubbleStackView extends FrameLayout
removeView(mDismissView);
}
mDismissView = new DismissView(getContext());
+ DismissViewUtils.setup(mDismissView);
int elevation = getResources().getDimensionPixelSize(R.dimen.bubble_elevation);
addView(mDismissView);
@@ -1494,9 +1500,6 @@ public class BubbleStackView extends FrameLayout
getViewTreeObserver().removeOnPreDrawListener(mViewUpdater);
getViewTreeObserver().removeOnDrawListener(mSystemGestureExcludeUpdater);
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- if (mBubbleOverflow != null) {
- mBubbleOverflow.cleanUpExpandedState();
- }
}
@Override
@@ -3411,6 +3414,19 @@ public class BubbleStackView extends FrameLayout
}
/**
+ * Removes the overflow view from the stack. This allows for re-adding it later to a new stack.
+ */
+ void resetOverflowView() {
+ BadgedImageView overflowIcon = mBubbleOverflow.getIconView();
+ if (overflowIcon != null) {
+ PhysicsAnimationLayout parent = (PhysicsAnimationLayout) overflowIcon.getParent();
+ if (parent != null) {
+ parent.removeViewNoAnimation(overflowIcon);
+ }
+ }
+ }
+
+ /**
* Holds some commonly queried information about the stack.
*/
public static class StackViewState {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 7a5815994dd0..da4a9898a44c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.bubbles;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
@@ -110,6 +111,9 @@ public class BubbleTaskViewHelper {
try {
options.setTaskAlwaysOnTop(true);
options.setLaunchedFromBubble(true);
+ options.setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
Intent fillInIntent = new Intent();
// Apply flags to make behaviour match documentLaunchMode=always.
@@ -117,11 +121,19 @@ public class BubbleTaskViewHelper {
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
if (mBubble.isAppBubble()) {
- PendingIntent pi = PendingIntent.getActivity(mContext, 0,
- mBubble.getAppBubbleIntent(),
- PendingIntent.FLAG_MUTABLE,
- null);
- mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
+ Context context =
+ mContext.createContextAsUser(
+ mBubble.getUser(), Context.CONTEXT_RESTRICTED);
+ PendingIntent pi = PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ mBubble.getAppBubbleIntent()
+ .addFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
+ .addFlags(FLAG_ACTIVITY_MULTIPLE_TASK),
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
+ /* options= */ null);
+ mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
+ launchBounds);
} else if (mBubble.hasMetadataShortcutId()) {
options.setApplyActivityFlagsForBubbles(true);
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 8ab9841ff0c2..66e69300f45f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -104,7 +104,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
@Override
protected BubbleViewInfo doInBackground(Void... voids) {
- if (mController.get().isShowingAsBubbleBar()) {
+ if (!verifyState()) {
+ // If we're in an inconsistent state, then switched modes and should just bail now.
+ return null;
+ }
+ if (mLayerView.get() != null) {
return BubbleViewInfo.populateForBubbleBar(mContext.get(), mController.get(),
mLayerView.get(), mIconFactory, mBubble, mSkipInflation);
} else {
@@ -118,7 +122,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
if (isCancelled() || viewInfo == null) {
return;
}
+
mMainExecutor.execute(() -> {
+ if (!verifyState()) {
+ return;
+ }
mBubble.setViewInfo(viewInfo);
if (mCallback != null) {
mCallback.onBubbleViewsReady(mBubble);
@@ -126,6 +134,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
});
}
+ private boolean verifyState() {
+ if (mController.get().isShowingAsBubbleBar()) {
+ return mLayerView.get() != null;
+ } else {
+ return mStackView.get() != null;
+ }
+ }
+
/**
* Info necessary to render a bubble.
*/
@@ -160,39 +176,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
LayoutInflater inflater = LayoutInflater.from(c);
info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate(
R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */);
- info.bubbleBarExpandedView.initialize(controller);
- }
-
- if (b.getShortcutInfo() != null) {
- info.shortcutInfo = b.getShortcutInfo();
+ info.bubbleBarExpandedView.initialize(controller, false /* isOverflow */);
}
- // App name & app icon
- PackageManager pm = BubbleController.getPackageManagerForUser(c,
- b.getUser().getIdentifier());
- ApplicationInfo appInfo;
- Drawable badgedIcon;
- Drawable appIcon;
- try {
- appInfo = pm.getApplicationInfo(
- b.getPackageName(),
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE);
- if (appInfo != null) {
- info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
- }
- appIcon = pm.getApplicationIcon(b.getPackageName());
- badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser());
- } catch (PackageManager.NameNotFoundException exception) {
- // If we can't find package... don't think we should show the bubble.
- Log.w(TAG, "Unable to find package: " + b.getPackageName());
+ if (!populateCommonInfo(info, c, b, iconFactory)) {
+ // if we failed to update common fields return null
return null;
}
- info.rawBadgeBitmap = iconFactory.getBadgeBitmap(badgedIcon, false).icon;
-
return info;
}
@@ -215,66 +206,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
info.expandedView.initialize(controller, stackView, false /* isOverflow */);
}
- if (b.getShortcutInfo() != null) {
- info.shortcutInfo = b.getShortcutInfo();
- }
-
- // App name & app icon
- PackageManager pm = BubbleController.getPackageManagerForUser(c,
- b.getUser().getIdentifier());
- ApplicationInfo appInfo;
- Drawable badgedIcon;
- Drawable appIcon;
- try {
- appInfo = pm.getApplicationInfo(
- b.getPackageName(),
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE);
- if (appInfo != null) {
- info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
- }
- appIcon = pm.getApplicationIcon(b.getPackageName());
- badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser());
- } catch (PackageManager.NameNotFoundException exception) {
- // If we can't find package... don't think we should show the bubble.
- Log.w(TAG, "Unable to find package: " + b.getPackageName());
+ if (!populateCommonInfo(info, c, b, iconFactory)) {
+ // if we failed to update common fields return null
return null;
}
- // Badged bubble image
- Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
- b.getIcon());
- if (bubbleDrawable == null) {
- // Default to app icon
- bubbleDrawable = appIcon;
- }
-
- BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
- b.isImportantConversation());
- info.badgeBitmap = badgeBitmapInfo.icon;
- // Raw badge bitmap never includes the important conversation ring
- info.rawBadgeBitmap = b.isImportantConversation()
- ? iconFactory.getBadgeBitmap(badgedIcon, false).icon
- : badgeBitmapInfo.icon;
-
- float[] bubbleBitmapScale = new float[1];
- info.bubbleBitmap = iconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale);
-
- // Dot color & placement
- Path iconPath = PathParser.createPathFromPathData(
- c.getResources().getString(com.android.internal.R.string.config_icon_mask));
- Matrix matrix = new Matrix();
- float scale = bubbleBitmapScale[0];
- float radius = DEFAULT_PATH_SIZE / 2f;
- matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
- radius /* pivot y */);
- iconPath.transform(matrix);
- info.dotPath = iconPath;
- info.dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
- Color.WHITE, WHITE_SCRIM_ALPHA);
-
// Flyout
info.flyoutMessage = b.getFlyoutMessage();
if (info.flyoutMessage != null) {
@@ -285,6 +221,75 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
}
}
+ /**
+ * Modifies the given {@code info} object and populates common fields in it.
+ *
+ * <p>This method returns {@code true} if the update was successful and {@code false} otherwise.
+ * Callers should assume that the info object is unusable if the update was unsuccessful.
+ */
+ private static boolean populateCommonInfo(
+ BubbleViewInfo info, Context c, Bubble b, BubbleIconFactory iconFactory) {
+ if (b.getShortcutInfo() != null) {
+ info.shortcutInfo = b.getShortcutInfo();
+ }
+
+ // App name & app icon
+ PackageManager pm = BubbleController.getPackageManagerForUser(c,
+ b.getUser().getIdentifier());
+ ApplicationInfo appInfo;
+ Drawable badgedIcon;
+ Drawable appIcon;
+ try {
+ appInfo = pm.getApplicationInfo(
+ b.getPackageName(),
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (appInfo != null) {
+ info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
+ }
+ appIcon = pm.getApplicationIcon(b.getPackageName());
+ badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser());
+ } catch (PackageManager.NameNotFoundException exception) {
+ // If we can't find package... don't think we should show the bubble.
+ Log.w(TAG, "Unable to find package: " + b.getPackageName());
+ return false;
+ }
+
+ // Badged bubble image
+ Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo, b.getIcon());
+ if (bubbleDrawable == null) {
+ // Default to app icon
+ bubbleDrawable = appIcon;
+ }
+
+ BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
+ b.isImportantConversation());
+ info.badgeBitmap = badgeBitmapInfo.icon;
+ // Raw badge bitmap never includes the important conversation ring
+ info.rawBadgeBitmap = b.isImportantConversation() // is this needed for bar?
+ ? iconFactory.getBadgeBitmap(badgedIcon, false).icon
+ : badgeBitmapInfo.icon;
+
+ float[] bubbleBitmapScale = new float[1];
+ info.bubbleBitmap = iconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale);
+
+ // Dot color & placement
+ Path iconPath = PathParser.createPathFromPathData(
+ c.getResources().getString(com.android.internal.R.string.config_icon_mask));
+ Matrix matrix = new Matrix();
+ float scale = bubbleBitmapScale[0];
+ float radius = DEFAULT_PATH_SIZE / 2f;
+ matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+ radius /* pivot y */);
+ iconPath.transform(matrix);
+ info.dotPath = iconPath;
+ info.dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
+ Color.WHITE, WHITE_SCRIM_ALPHA);
+ return true;
+ }
+
@Nullable
static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) {
Objects.requireNonNull(context);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 4d329dd5d446..759246eb285d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -60,9 +60,11 @@ public interface Bubbles {
DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
- DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED})
+ DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED,
+ DISMISS_SWITCH_TO_STACK})
@Target({FIELD, LOCAL_VARIABLE, PARAMETER})
- @interface DismissReason {}
+ @interface DismissReason {
+ }
int DISMISS_USER_GESTURE = 1;
int DISMISS_AGED = 2;
@@ -80,6 +82,7 @@ public interface Bubbles {
int DISMISS_NO_BUBBLE_UP = 14;
int DISMISS_RELOAD_FROM_DISK = 15;
int DISMISS_USER_REMOVED = 16;
+ int DISMISS_SWITCH_TO_STACK = 17;
/** Returns a binder that can be passed to an external process to manipulate Bubbles. */
default IBubbles createExternalInterface() {
@@ -120,8 +123,8 @@ public interface Bubbles {
/**
* This method has different behavior depending on:
- * - if an app bubble exists
- * - if an app bubble is expanded
+ * - if an app bubble exists
+ * - if an app bubble is expanded
*
* If no app bubble exists, this will add and expand a bubble with the provided intent. The
* intent must be explicit (i.e. include a package name or fully qualified component class name)
@@ -135,13 +138,13 @@ public interface Bubbles {
* the bubble or bubble stack.
*
* Some notes:
- * - Only one app bubble is supported at a time, regardless of users. Multi-users support is
- * tracked in b/273533235.
- * - Calling this method with a different intent than the existing app bubble will do nothing
+ * - Only one app bubble is supported at a time, regardless of users. Multi-users support is
+ * tracked in b/273533235.
+ * - Calling this method with a different intent than the existing app bubble will do nothing
*
* @param intent the intent to display in the bubble expanded view.
- * @param user the {@link UserHandle} of the user to start this activity for.
- * @param icon the {@link Icon} to use for the bubble view.
+ * @param user the {@link UserHandle} of the user to start this activity for.
+ * @param icon the {@link Icon} to use for the bubble view.
*/
void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon);
@@ -172,13 +175,12 @@ public interface Bubbles {
* {@link Bubble#setSuppressNotification}. For the case of suppressed summaries, we also add
* {@link BubbleData#addSummaryToSuppress}.
*
- * @param entry the notification of the BubbleEntry should be removed.
- * @param children the list of child notification of the BubbleEntry from 1st param entry,
- * this will be null if entry does have no children.
+ * @param entry the notification of the BubbleEntry should be removed.
+ * @param children the list of child notification of the BubbleEntry from 1st param entry,
+ * this will be null if entry does have no children.
* @param removeCallback the remove callback for SystemUI side to remove notification, the int
* number should be list position of children list and use -1 for
* removing the parent notification.
- *
* @return true if we want to intercept the dismissal of the entry, else false.
*/
boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children,
@@ -200,9 +202,9 @@ public interface Bubbles {
/**
* Called when new notification entry updated.
*
- * @param entry the {@link BubbleEntry} by the notification.
+ * @param entry the {@link BubbleEntry} by the notification.
* @param shouldBubbleUp {@code true} if this notification should bubble up.
- * @param fromSystem {@code true} if this update is from NotificationManagerService.
+ * @param fromSystem {@code true} if this update is from NotificationManagerService.
*/
void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem);
@@ -218,7 +220,7 @@ public interface Bubbles {
* filtering and sorting. This is used to dismiss or create bubbles based on changes in
* permissions on the notification channel or the global setting.
*
- * @param rankingMap the updated ranking map from NotificationListenerService
+ * @param rankingMap the updated ranking map from NotificationListenerService
* @param entryDataByKey a map of ranking key to bubble entry and whether the entry should
* bubble up
*/
@@ -230,9 +232,9 @@ public interface Bubbles {
* Called when a notification channel is modified, in response to
* {@link NotificationListenerService#onNotificationChannelModified}.
*
- * @param pkg the package the notification channel belongs to.
- * @param user the user the notification channel belongs to.
- * @param channel the channel being modified.
+ * @param pkg the package the notification channel belongs to.
+ * @param user the user the notification channel belongs to.
+ * @param channel the channel being modified.
* @param modificationType the type of modification that occurred to the channel.
*/
void onNotificationChannelModified(
@@ -300,7 +302,7 @@ public interface Bubbles {
* Called when the expansion state of the bubble stack changes.
*
* @param isExpanding whether it's expanding or collapsing
- * @param key the notification key associated with bubble being expanded
+ * @param key the notification key associated with bubble being expanded
*/
void onBubbleExpandChanged(boolean isExpanding, String key);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
new file mode 100644
index 000000000000..ed3624035757
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("DismissViewUtils")
+
+package com.android.wm.shell.bubbles
+
+import com.android.wm.shell.R
+import com.android.wm.shell.common.bubbles.DismissView
+
+fun DismissView.setup() {
+ setup(DismissView.Config(
+ targetSizeResId = R.dimen.dismiss_circle_size,
+ iconSizeResId = R.dimen.dismiss_target_x_size,
+ bottomMarginResId = R.dimen.floating_dismiss_bottom_margin,
+ floatingGradientHeightResId = R.dimen.floating_dismiss_gradient_height,
+ floatingGradientColorResId = android.R.color.system_neutral1_900,
+ backgroundResId = R.drawable.dismiss_circle_background,
+ iconResId = R.drawable.pip_ic_close_white
+ ))
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 862e818a998b..20ae8469f431 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -35,6 +35,4 @@ interface IBubbles {
oneway void collapseBubbles() = 5;
- oneway void onTaskbarStateChanged(in int newState) = 6;
-
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
index beb1c5fa6c10..f3cc514d2972 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
@@ -327,6 +327,12 @@ public class PhysicsAnimationLayout extends FrameLayout {
addViewInternal(child, index, params, false /* isReorder */);
}
+ /** Removes the child view immediately. */
+ public void removeViewNoAnimation(View view) {
+ super.removeView(view);
+ view.setTag(R.id.physics_animator_tag, null);
+ }
+
@Override
public void removeView(View view) {
if (mController != null) {
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 23f65f943aa4..f729d029a818 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
@@ -27,6 +27,7 @@ import android.widget.FrameLayout;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
@@ -215,9 +216,10 @@ public class BubbleBarAnimationHelper {
}
BubbleBarExpandedView bbev = mExpandedBubble.getBubbleBarExpandedView();
+ boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
final int padding = mPositioner.getBubbleBarExpandedViewPadding();
- final int width = mPositioner.getExpandedViewWidthForBubbleBar();
- final int height = mPositioner.getExpandedViewHeightForBubbleBar();
+ final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
+ final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) bbev.getLayoutParams();
lp.width = width;
lp.height = height;
@@ -227,7 +229,8 @@ public class BubbleBarAnimationHelper {
} else {
bbev.setX(mPositioner.getAvailableRect().width() - width - padding);
}
- bbev.setY(mPositioner.getInsets().top + padding);
+ bbev.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
bbev.updateLocation();
+ bbev.maybeShowOverflow();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index b8f049becb6f..396aa0e56cf6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -16,12 +16,16 @@
package com.android.wm.shell.bubbles.bar;
+import android.annotation.ColorInt;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Outline;
+import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
@@ -30,9 +34,14 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.BubbleOverflowContainerView;
import com.android.wm.shell.bubbles.BubbleTaskViewHelper;
+import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.taskview.TaskView;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
/**
* Expanded view of a bubble when it's part of the bubble bar.
*
@@ -44,12 +53,17 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
private static final int INVALID_TASK_ID = -1;
private BubbleController mController;
+ private boolean mIsOverflow;
private BubbleTaskViewHelper mBubbleTaskViewHelper;
+ private BubbleBarMenuViewController mMenuViewController;
+ private @Nullable Supplier<Rect> mLayerBoundsSupplier;
+ private @Nullable Consumer<String> mUnBubbleConversationCallback;
- private HandleView mMenuView;
- private TaskView mTaskView;
+ private BubbleBarHandleView mHandleView = new BubbleBarHandleView(getContext());
+ private @Nullable TaskView mTaskView;
+ private @Nullable BubbleOverflowContainerView mOverflowView;
- private int mMenuHeight;
+ private int mHandleHeight;
private int mBackgroundColor;
private float mCornerRadius = 0f;
@@ -83,11 +97,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
super.onFinishInflate();
Context context = getContext();
setElevation(getResources().getDimensionPixelSize(R.dimen.bubble_elevation));
- mMenuHeight = context.getResources().getDimensionPixelSize(
- R.dimen.bubblebar_expanded_view_menu_size);
- mMenuView = new HandleView(context);
- addView(mMenuView);
-
+ mHandleHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_handle_size);
+ addView(mHandleView);
applyThemeAttrs();
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@@ -98,23 +110,71 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
});
}
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ // Hide manage menu when view disappears
+ mMenuViewController.hideMenu(false /* animated */);
+ }
+
/** Set the BubbleController on the view, must be called before doing anything else. */
- public void initialize(BubbleController controller) {
+ public void initialize(BubbleController controller, boolean isOverflow) {
mController = controller;
- mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, mController,
- /* listener= */ this,
- /* viewParent= */ this);
- mTaskView = mBubbleTaskViewHelper.getTaskView();
- addView(mTaskView);
- mTaskView.setEnableSurfaceClipping(true);
- mTaskView.setCornerRadius(mCornerRadius);
+ mIsOverflow = isOverflow;
+
+ if (mIsOverflow) {
+ mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate(
+ R.layout.bubble_overflow_container, null /* root */);
+ mOverflowView.setBubbleController(mController);
+ addView(mOverflowView);
+ } else {
+
+ mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, mController,
+ /* listener= */ this,
+ /* viewParent= */ this);
+ mTaskView = mBubbleTaskViewHelper.getTaskView();
+ addView(mTaskView);
+ mTaskView.setEnableSurfaceClipping(true);
+ mTaskView.setCornerRadius(mCornerRadius);
+ }
+ mMenuViewController = new BubbleBarMenuViewController(mContext, this);
+ mMenuViewController.setListener(new BubbleBarMenuViewController.Listener() {
+ @Override
+ public void onMenuVisibilityChanged(boolean visible) {
+ if (mTaskView == null || mLayerBoundsSupplier == null) return;
+ // Updates the obscured touchable region for the task surface.
+ mTaskView.setObscuredTouchRect(visible ? mLayerBoundsSupplier.get() : null);
+ }
+
+ @Override
+ public void onUnBubbleConversation(Bubble bubble) {
+ if (mUnBubbleConversationCallback != null) {
+ mUnBubbleConversationCallback.accept(bubble.getKey());
+ }
+ }
+
+ @Override
+ public void onOpenAppSettings(Bubble bubble) {
+ mController.collapseStack();
+ mContext.startActivityAsUser(bubble.getSettingsIntent(mContext), bubble.getUser());
+ }
+
+ @Override
+ public void onDismissBubble(Bubble bubble) {
+ mController.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED);
+ }
+ });
+ mHandleView.setOnClickListener(view -> {
+ mMenuViewController.showMenu(true /* animated */);
+ });
}
// TODO (b/275087636): call this when theme/config changes
- void applyThemeAttrs() {
+ /** Updates the view based on the current theme. */
+ public void applyThemeAttrs() {
boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
mContext.getResources());
- final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
+ final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
android.R.attr.dialogCornerRadius,
android.R.attr.colorBackgroundFloating});
mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
@@ -123,14 +183,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
ta.recycle();
- mMenuView.setCornerRadius(mCornerRadius);
- mMenuHeight = getResources().getDimensionPixelSize(
- R.dimen.bubblebar_expanded_view_menu_size);
+ mHandleHeight = getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_handle_size);
if (mTaskView != null) {
mTaskView.setCornerRadius(mCornerRadius);
- mTaskView.setElevation(150);
- updateMenuColor();
+ updateHandleAndBackgroundColor(true /* animated */);
}
}
@@ -138,10 +196,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
-
- // Add corner radius here so that the menu extends behind the rounded corners of TaskView.
- int menuViewHeight = Math.min((int) (mMenuHeight + mCornerRadius), height);
- measureChild(mMenuView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight,
+ int menuViewHeight = Math.min(mHandleHeight, height);
+ measureChild(mHandleView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight,
MeasureSpec.getMode(heightMeasureSpec)));
if (mTaskView != null) {
@@ -153,12 +209,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
// Drag handle above
- final int dragHandleBottom = t + mMenuView.getMeasuredHeight();
- mMenuView.layout(l, t, r, dragHandleBottom);
+ final int dragHandleBottom = t + mHandleView.getMeasuredHeight();
+ mHandleView.layout(l, t, r, dragHandleBottom);
if (mTaskView != null) {
- // Subtract radius so that the menu extends behind the rounded corners of TaskView.
- mTaskView.layout(l, (int) (dragHandleBottom - mCornerRadius), r,
+ mTaskView.layout(l, dragHandleBottom, r,
dragHandleBottom + mTaskView.getMeasuredHeight());
}
}
@@ -166,7 +222,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
@Override
public void onTaskCreated() {
setContentVisibility(true);
- updateMenuColor();
+ updateHandleAndBackgroundColor(false /* animated */);
}
@Override
@@ -187,11 +243,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
}
mBubbleTaskViewHelper.cleanUpTaskView();
}
+ mMenuViewController.hideMenu(false /* animated */);
}
- /** Updates the bubble shown in this task view. */
+ /** Updates the bubble shown in the expanded view. */
public void update(Bubble bubble) {
mBubbleTaskViewHelper.update(bubble);
+ mMenuViewController.updateMenu(bubble);
}
/** The task id of the activity shown in the task view, if it exists. */
@@ -199,12 +257,33 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
return mBubbleTaskViewHelper != null ? mBubbleTaskViewHelper.getTaskId() : INVALID_TASK_ID;
}
+ /** Sets layer bounds supplier used for obscured touchable region of task view */
+ void setLayerBoundsSupplier(@Nullable Supplier<Rect> supplier) {
+ mLayerBoundsSupplier = supplier;
+ }
+
+ /** Sets the function to call to un-bubble the given conversation. */
+ public void setUnBubbleConversationCallback(
+ @Nullable Consumer<String> unBubbleConversationCallback) {
+ mUnBubbleConversationCallback = unBubbleConversationCallback;
+ }
+
/**
* Call when the location or size of the view has changed to update TaskView.
*/
public void updateLocation() {
- if (mTaskView == null) return;
- mTaskView.onLocationChanged();
+ if (mTaskView != null) {
+ mTaskView.onLocationChanged();
+ }
+ }
+
+ /** Shows the expanded view for the overflow if it exists. */
+ void maybeShowOverflow() {
+ if (mOverflowView != null) {
+ // post this to the looper so that the view has a chance to be laid out before it can
+ // calculate row and column sizes correctly.
+ post(() -> mOverflowView.show());
+ }
}
/** Sets the alpha of the task view. */
@@ -218,16 +297,33 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
}
}
- /** Updates the menu bar to be the status bar color specified by the app. */
- private void updateMenuColor() {
+ /**
+ * Updates the background color to match with task view status/bg color, and sets handle color
+ * to contrast with the background
+ */
+ private void updateHandleAndBackgroundColor(boolean animated) {
if (mTaskView == null) return;
- ActivityManager.RunningTaskInfo info = mTaskView.getTaskInfo();
- final int taskBgColor = info.taskDescription.getStatusBarColor();
- final int color = Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
- if (color != -1) {
- mMenuView.setBackgroundColor(color);
+ final int color = getTaskViewColor();
+ final boolean isRegionDark = Color.luminance(color) <= 0.5;
+ mHandleView.updateHandleColor(isRegionDark, animated);
+ setBackgroundColor(color);
+ }
+
+ /**
+ * Retrieves task view status/nav bar color or background if available
+ *
+ * TODO (b/283075226): Update with color sampling when
+ * RegionSamplingHelper or alternative is available
+ */
+ private @ColorInt int getTaskViewColor() {
+ if (mTaskView == null || mTaskView.getTaskInfo() == null) return mBackgroundColor;
+ ActivityManager.TaskDescription taskDescription = mTaskView.getTaskInfo().taskDescription;
+ if (taskDescription.getStatusBarColor() != Color.TRANSPARENT) {
+ return taskDescription.getStatusBarColor();
+ } else if (taskDescription.getBackgroundColor() != Color.TRANSPARENT) {
+ return taskDescription.getBackgroundColor();
} else {
- mMenuView.setBackgroundColor(mBackgroundColor);
+ return mBackgroundColor;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
new file mode 100644
index 000000000000..ce26bc0322b3
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.bubbles.bar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import androidx.annotation.ColorInt;
+import androidx.core.content.ContextCompat;
+
+import com.android.wm.shell.R;
+
+/**
+ * Handle view to show at the top of a bubble bar expanded view.
+ */
+public class BubbleBarHandleView extends View {
+ private static final long COLOR_CHANGE_DURATION = 120;
+
+ private int mHandleWidth;
+ private int mHandleHeight;
+ private @ColorInt int mHandleLightColor;
+ private @ColorInt int mHandleDarkColor;
+ private @Nullable ObjectAnimator mColorChangeAnim;
+
+ public BubbleBarHandleView(Context context) {
+ this(context, null /* attrs */);
+ }
+
+ public BubbleBarHandleView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0 /* defStyleAttr */);
+ }
+
+ public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0 /* defStyleRes */);
+ }
+
+ public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ mHandleWidth = getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_handle_width);
+ mHandleHeight = getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_handle_height);
+ mHandleLightColor = ContextCompat.getColor(getContext(),
+ R.color.bubble_bar_expanded_view_handle_light);
+ mHandleDarkColor = ContextCompat.getColor(getContext(),
+ R.color.bubble_bar_expanded_view_handle_dark);
+
+ setClipToOutline(true);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ final int handleCenterX = view.getWidth() / 2;
+ final int handleCenterY = view.getHeight() / 2;
+ final float handleRadius = mHandleHeight / 2f;
+ Rect handleBounds = new Rect(
+ handleCenterX - mHandleWidth / 2,
+ handleCenterY - mHandleHeight / 2,
+ handleCenterX + mHandleWidth / 2,
+ handleCenterY + mHandleHeight / 2);
+ outline.setRoundRect(handleBounds, handleRadius);
+ }
+ });
+ }
+
+ /**
+ * Updates the handle color.
+ *
+ * @param isRegionDark Whether the background behind the handle is dark, and thus the handle
+ * should be light (and vice versa).
+ * @param animated Whether to animate the change, or apply it immediately.
+ */
+ public void updateHandleColor(boolean isRegionDark, boolean animated) {
+ int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor;
+ if (mColorChangeAnim != null) {
+ mColorChangeAnim.cancel();
+ }
+ if (animated) {
+ mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor);
+ mColorChangeAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mColorChangeAnim = null;
+ }
+ });
+ mColorChangeAnim.setDuration(COLOR_CHANGE_DURATION);
+ mColorChangeAnim.start();
+ } else {
+ setBackgroundColor(newColor);
+ }
+ }
+}
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 b1a725b6e5c4..d20b33edf2c9 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
@@ -29,9 +29,12 @@ import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
+import java.util.function.Consumer;
+
/**
* Similar to {@link com.android.wm.shell.bubbles.BubbleStackView}, this view is added to window
* manager to display bubbles. However, it is only used when bubbles are being displayed in
@@ -53,6 +56,7 @@ public class BubbleBarLayerView extends FrameLayout
@Nullable
private BubbleViewProvider mExpandedBubble;
private BubbleBarExpandedView mExpandedView;
+ private @Nullable Consumer<String> mUnBubbleConversationCallback;
// TODO(b/273310265) - currently the view is always on the right, need to update for RTL.
/** Whether the expanded view is displaying on the left of the screen or not. */
@@ -143,9 +147,18 @@ public class BubbleBarLayerView extends FrameLayout
if (mExpandedView == null) {
mExpandedBubble = b;
mExpandedView = expandedView;
- final int width = mPositioner.getExpandedViewWidthForBubbleBar();
- final int height = mPositioner.getExpandedViewHeightForBubbleBar();
+ boolean isOverflowExpanded = b.getKey().equals(BubbleOverflow.KEY);
+ final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
+ final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
mExpandedView.setVisibility(GONE);
+ mExpandedView.setUnBubbleConversationCallback(mUnBubbleConversationCallback);
+ mExpandedView.setLayerBoundsSupplier(() -> new Rect(0, 0, getWidth(), getHeight()));
+ mExpandedView.setUnBubbleConversationCallback(bubbleKey -> {
+ if (mUnBubbleConversationCallback != null) {
+ mUnBubbleConversationCallback.accept(bubbleKey);
+ }
+ });
+ mExpandedView.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
addView(mExpandedView, new FrameLayout.LayoutParams(width, height));
}
@@ -165,12 +178,19 @@ public class BubbleBarLayerView extends FrameLayout
showScrim(false);
}
+ /** Sets the function to call to un-bubble the given conversation. */
+ public void setUnBubbleConversationCallback(
+ @Nullable Consumer<String> unBubbleConversationCallback) {
+ mUnBubbleConversationCallback = unBubbleConversationCallback;
+ }
+
/** Updates the expanded view size and position. */
private void updateExpandedView() {
if (mExpandedView == null) return;
+ boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
final int padding = mPositioner.getBubbleBarExpandedViewPadding();
- final int width = mPositioner.getExpandedViewWidthForBubbleBar();
- final int height = mPositioner.getExpandedViewHeightForBubbleBar();
+ final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
+ final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
FrameLayout.LayoutParams lp = (LayoutParams) mExpandedView.getLayoutParams();
lp.width = width;
lp.height = height;
@@ -180,7 +200,7 @@ public class BubbleBarLayerView extends FrameLayout
} else {
mExpandedView.setX(mPositioner.getAvailableRect().width() - width - padding);
}
- mExpandedView.setY(mPositioner.getInsets().top + padding);
+ mExpandedView.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
mExpandedView.updateLocation();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
new file mode 100644
index 000000000000..00b977721bea
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.bubbles.bar;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.wm.shell.R;
+
+/**
+ * Bubble bar expanded view menu item view to display menu action details
+ */
+public class BubbleBarMenuItemView extends LinearLayout {
+ private ImageView mImageView;
+ private TextView mTextView;
+
+ public BubbleBarMenuItemView(Context context) {
+ this(context, null /* attrs */);
+ }
+
+ public BubbleBarMenuItemView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0 /* defStyleAttr */);
+ }
+
+ public BubbleBarMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0 /* defStyleRes */);
+ }
+
+ public BubbleBarMenuItemView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mImageView = findViewById(R.id.bubble_bar_menu_item_icon);
+ mTextView = findViewById(R.id.bubble_bar_menu_item_title);
+ }
+
+ /**
+ * Update menu item with the details and tint color
+ */
+ void update(Icon icon, String title, @ColorInt int tint) {
+ if (tint == Color.TRANSPARENT) {
+ final TypedArray typedArray = getContext().obtainStyledAttributes(
+ new int[]{android.R.attr.textColorPrimary});
+ mTextView.setTextColor(typedArray.getColor(0, Color.BLACK));
+ } else {
+ icon.setTint(tint);
+ mTextView.setTextColor(tint);
+ }
+
+ mImageView.setImageIcon(icon);
+ mTextView.setText(title);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
new file mode 100644
index 000000000000..211fe0d48e43
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.bubbles.bar;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.bubbles.Bubble;
+
+import java.util.ArrayList;
+
+/**
+ * Bubble bar expanded view menu
+ */
+public class BubbleBarMenuView extends LinearLayout {
+ private ViewGroup mBubbleSectionView;
+ private ViewGroup mActionsSectionView;
+ private ImageView mBubbleIconView;
+ private TextView mBubbleTitleView;
+
+ public BubbleBarMenuView(Context context) {
+ this(context, null /* attrs */);
+ }
+
+ public BubbleBarMenuView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0 /* defStyleAttr */);
+ }
+
+ public BubbleBarMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0 /* defStyleRes */);
+ }
+
+ public BubbleBarMenuView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mBubbleSectionView = findViewById(R.id.bubble_bar_manage_menu_bubble_section);
+ mActionsSectionView = findViewById(R.id.bubble_bar_manage_menu_actions_section);
+ mBubbleIconView = findViewById(R.id.bubble_bar_manage_menu_bubble_icon);
+ mBubbleTitleView = findViewById(R.id.bubble_bar_manage_menu_bubble_title);
+ }
+
+ /** Update menu details with bubble info */
+ void updateInfo(Bubble bubble) {
+ if (bubble.getIcon() != null) {
+ mBubbleIconView.setImageIcon(bubble.getIcon());
+ } else {
+ mBubbleIconView.setImageBitmap(bubble.getBubbleIcon());
+ }
+ mBubbleTitleView.setText(bubble.getTitle());
+ }
+
+ /**
+ * Update menu action items views
+ * @param actions used to populate menu item views
+ */
+ void updateActions(ArrayList<MenuAction> actions) {
+ mActionsSectionView.removeAllViews();
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+
+ for (MenuAction action : actions) {
+ BubbleBarMenuItemView itemView = (BubbleBarMenuItemView) inflater.inflate(
+ R.layout.bubble_bar_menu_item, mActionsSectionView, false);
+ itemView.update(action.mIcon, action.mTitle, action.mTint);
+ itemView.setOnClickListener(action.mOnClick);
+ mActionsSectionView.addView(itemView);
+ }
+ }
+
+ /** Sets on close menu listener */
+ void setOnCloseListener(Runnable onClose) {
+ mBubbleSectionView.setOnClickListener(view -> {
+ onClose.run();
+ });
+ }
+
+ /**
+ * Overridden to proxy to section views alpha.
+ * @implNote
+ * If animate alpha on the parent (menu container) view, section view shadows get distorted.
+ * To prevent distortion and artifacts alpha changes applied directly on the section views.
+ */
+ @Override
+ public void setAlpha(float alpha) {
+ mBubbleSectionView.setAlpha(alpha);
+ mActionsSectionView.setAlpha(alpha);
+ }
+
+ /**
+ * Overridden to proxy section view alpha value.
+ * @implNote
+ * The assumption is that both section views have the same alpha value
+ */
+ @Override
+ public float getAlpha() {
+ return mBubbleSectionView.getAlpha();
+ }
+
+ /**
+ * Menu action details used to create menu items
+ */
+ static class MenuAction {
+ private Icon mIcon;
+ private @ColorInt int mTint;
+ private String mTitle;
+ private OnClickListener mOnClick;
+
+ MenuAction(Icon icon, String title, OnClickListener onClick) {
+ this(icon, title, Color.TRANSPARENT, onClick);
+ }
+
+ MenuAction(Icon icon, String title, @ColorInt int tint, OnClickListener onClick) {
+ this.mIcon = icon;
+ this.mTitle = title;
+ this.mTint = tint;
+ this.mOnClick = onClick;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
new file mode 100644
index 000000000000..8be140c16435
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.bubbles.bar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Icon;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.core.content.ContextCompat;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.Bubble;
+
+import java.util.ArrayList;
+
+/**
+ * Manages bubble bar expanded view menu presentation and animations
+ */
+class BubbleBarMenuViewController {
+ private static final float MENU_INITIAL_SCALE = 0.5f;
+ private final Context mContext;
+ private final ViewGroup mRootView;
+ private @Nullable Listener mListener;
+ private @Nullable Bubble mBubble;
+ private @Nullable BubbleBarMenuView mMenuView;
+ /** A transparent view used to intercept touches to collapse menu when presented */
+ private @Nullable View mScrimView;
+ private @Nullable PhysicsAnimator<BubbleBarMenuView> mMenuAnimator;
+ private PhysicsAnimator.SpringConfig mMenuSpringConfig;
+
+ BubbleBarMenuViewController(Context context, ViewGroup rootView) {
+ mContext = context;
+ mRootView = rootView;
+ mMenuSpringConfig = new PhysicsAnimator.SpringConfig(
+ SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+ }
+
+ /** Sets menu actions listener */
+ void setListener(@Nullable Listener listener) {
+ mListener = listener;
+ }
+
+ /** Update menu with bubble */
+ void updateMenu(@NonNull Bubble bubble) {
+ mBubble = bubble;
+ }
+
+ /**
+ * Show bubble bar expanded view menu
+ * @param animated if should animate transition
+ */
+ void showMenu(boolean animated) {
+ if (mMenuView == null || mScrimView == null) {
+ setupMenu();
+ }
+ cancelAnimations();
+ mMenuView.setVisibility(View.VISIBLE);
+ mScrimView.setVisibility(View.VISIBLE);
+ Runnable endActions = () -> {
+ mMenuView.getChildAt(0).requestAccessibilityFocus();
+ if (mListener != null) {
+ mListener.onMenuVisibilityChanged(true /* isShown */);
+ }
+ };
+ if (animated) {
+ animateTransition(true /* show */, endActions);
+ } else {
+ endActions.run();
+ }
+ }
+
+ /**
+ * Hide bubble bar expanded view menu
+ * @param animated if should animate transition
+ */
+ void hideMenu(boolean animated) {
+ if (mMenuView == null || mScrimView == null) return;
+ cancelAnimations();
+ Runnable endActions = () -> {
+ mMenuView.setVisibility(View.GONE);
+ mScrimView.setVisibility(View.GONE);
+ if (mListener != null) {
+ mListener.onMenuVisibilityChanged(false /* isShown */);
+ }
+ };
+ if (animated) {
+ animateTransition(false /* show */, endActions);
+ } else {
+ endActions.run();
+ }
+ }
+
+ /**
+ * Animate show/hide menu transition
+ * @param show if should show or hide the menu
+ * @param endActions will be called when animation ends
+ */
+ private void animateTransition(boolean show, Runnable endActions) {
+ if (mMenuView == null) return;
+ mMenuAnimator = PhysicsAnimator.getInstance(mMenuView);
+ mMenuAnimator.setDefaultSpringConfig(mMenuSpringConfig);
+ mMenuAnimator
+ .spring(DynamicAnimation.ALPHA, show ? 1f : 0f)
+ .spring(DynamicAnimation.SCALE_Y, show ? 1f : MENU_INITIAL_SCALE)
+ .withEndActions(() -> {
+ mMenuAnimator = null;
+ endActions.run();
+ })
+ .start();
+ }
+
+ /** Cancel running animations */
+ private void cancelAnimations() {
+ if (mMenuAnimator != null) {
+ mMenuAnimator.cancel();
+ mMenuAnimator = null;
+ }
+ }
+
+ /** Sets up and inflate menu views */
+ private void setupMenu() {
+ // Menu view setup
+ mMenuView = (BubbleBarMenuView) LayoutInflater.from(mContext).inflate(
+ R.layout.bubble_bar_menu_view, mRootView, false);
+ mMenuView.setAlpha(0f);
+ mMenuView.setPivotY(0f);
+ mMenuView.setScaleY(MENU_INITIAL_SCALE);
+ mMenuView.setOnCloseListener(() -> hideMenu(true /* animated */));
+ if (mBubble != null) {
+ mMenuView.updateInfo(mBubble);
+ mMenuView.updateActions(createMenuActions(mBubble));
+ }
+ // Scrim view setup
+ mScrimView = new View(mContext);
+ mScrimView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mScrimView.setOnClickListener(view -> hideMenu(true /* animated */));
+ // Attach to root view
+ mRootView.addView(mScrimView);
+ mRootView.addView(mMenuView);
+ }
+
+ /**
+ * Creates menu actions to populate menu view
+ * @param bubble used to create actions depending on bubble type
+ */
+ private ArrayList<BubbleBarMenuView.MenuAction> createMenuActions(Bubble bubble) {
+ ArrayList<BubbleBarMenuView.MenuAction> menuActions = new ArrayList<>();
+ Resources resources = mContext.getResources();
+
+ if (bubble.isConversation()) {
+ // Don't bubble conversation action
+ menuActions.add(new BubbleBarMenuView.MenuAction(
+ Icon.createWithResource(mContext, R.drawable.bubble_ic_stop_bubble),
+ resources.getString(R.string.bubbles_dont_bubble_conversation),
+ view -> {
+ hideMenu(true /* animated */);
+ if (mListener != null) {
+ mListener.onUnBubbleConversation(bubble);
+ }
+ }
+ ));
+ // Open settings action
+ Icon appIcon = bubble.getRawAppBadge() != null ? Icon.createWithBitmap(
+ bubble.getRawAppBadge()) : null;
+ menuActions.add(new BubbleBarMenuView.MenuAction(
+ appIcon,
+ resources.getString(R.string.bubbles_app_settings, bubble.getAppName()),
+ view -> {
+ hideMenu(true /* animated */);
+ if (mListener != null) {
+ mListener.onOpenAppSettings(bubble);
+ }
+ }
+ ));
+ }
+
+ // Dismiss bubble action
+ menuActions.add(new BubbleBarMenuView.MenuAction(
+ Icon.createWithResource(resources, R.drawable.ic_remove_no_shadow),
+ resources.getString(R.string.bubble_dismiss_text),
+ ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_menu_close),
+ view -> {
+ hideMenu(true /* animated */);
+ if (mListener != null) {
+ mListener.onDismissBubble(bubble);
+ }
+ }
+ ));
+
+ return menuActions;
+ }
+
+ /**
+ * Bubble bar expanded view menu actions listener
+ */
+ interface Listener {
+ /**
+ * Called when manage menu is shown/hidden
+ * If animated will be called when animation ends
+ */
+ void onMenuVisibilityChanged(boolean visible);
+
+ /**
+ * Un-bubbles conversation and removes the bubble from the stack
+ * This conversation will not be bubbled with new messages
+ * @see com.android.wm.shell.bubbles.BubbleController
+ */
+ void onUnBubbleConversation(Bubble bubble);
+
+ /**
+ * Launches app notification bubble settings for the bubble with intent created in:
+ * {@code Bubble.getSettingsIntent}
+ */
+ void onOpenAppSettings(Bubble bubble);
+
+ /**
+ * Dismiss bubble and remove it from the bubble stack
+ */
+ void onDismissBubble(Bubble bubble);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/HandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/HandleView.java
deleted file mode 100644
index 9ee8a9d98aa1..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/HandleView.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wm.shell.bubbles.bar;
-
-import android.content.Context;
-import android.view.Gravity;
-import android.widget.LinearLayout;
-
-/**
- * Handle / menu view to show at the top of a bubble bar expanded view.
- */
-public class HandleView extends LinearLayout {
-
- // TODO(b/273307221): implement the manage menu in this view.
- public HandleView(Context context) {
- super(context);
- setOrientation(LinearLayout.HORIZONTAL);
- setGravity(Gravity.CENTER);
- }
-
- /**
- * The menu extends past the top of the TaskView because of the rounded corners. This means
- * to center content in the menu we must subtract the radius (i.e. the amount of space covered
- * by TaskView).
- */
- public void setCornerRadius(float radius) {
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (int) radius);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt
new file mode 100644
index 000000000000..85aaa8ef585c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles.properties
+
+/**
+ * An interface for exposing bubble properties via flags which can be controlled easily in tests.
+ */
+interface BubbleProperties {
+ /**
+ * Whether bubble bar is enabled.
+ *
+ * When this is `true`, depending on additional factors, such as screen size and taskbar state,
+ * bubbles will be displayed in the bubble bar instead of floating.
+ *
+ * When this is `false`, bubbles will be floating.
+ */
+ val isBubbleBarEnabled: Boolean
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt
new file mode 100644
index 000000000000..9d8b9a6f3260
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles.properties
+
+import android.os.SystemProperties
+
+/** Provides bubble properties in production. */
+object ProdBubbleProperties : BubbleProperties {
+
+ // TODO(b/256873975) Should use proper flag when available to shell/launcher
+ override val isBubbleBarEnabled =
+ SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt
new file mode 100644
index 000000000000..81592c35e4ac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common
+
+import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG
+import com.android.wm.shell.util.KtProtoLog
+
+/**
+ * Controller to manage behavior of activities launched with
+ * [android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT].
+ */
+class LaunchAdjacentController(private val syncQueue: SyncTransactionQueue) {
+
+ /** Allows to temporarily disable launch adjacent handling */
+ var launchAdjacentEnabled: Boolean = true
+ set(value) {
+ if (field != value) {
+ KtProtoLog.d(WM_SHELL_TASK_ORG, "set launch adjacent flag root enabled=%b", value)
+ field = value
+ container?.let { c ->
+ if (value) {
+ enableContainer(c)
+ } else {
+ disableContainer((c))
+ }
+ }
+ }
+ }
+ private var container: WindowContainerToken? = null
+
+ /**
+ * Set [container] as the new launch adjacent flag root container.
+ *
+ * If launch adjacent handling is disabled through [setLaunchAdjacentEnabled], won't set the
+ * container until after it is enabled again.
+ *
+ * @see WindowContainerTransaction.setLaunchAdjacentFlagRoot
+ */
+ fun setLaunchAdjacentRoot(container: WindowContainerToken) {
+ KtProtoLog.d(WM_SHELL_TASK_ORG, "set new launch adjacent flag root container")
+ this.container = container
+ if (launchAdjacentEnabled) {
+ enableContainer(container)
+ }
+ }
+
+ /**
+ * Clear a container previously set through [setLaunchAdjacentRoot].
+ *
+ * Always clears the container, regardless of [launchAdjacentEnabled] value.
+ *
+ * @see WindowContainerTransaction.clearLaunchAdjacentFlagRoot
+ */
+ fun clearLaunchAdjacentRoot() {
+ KtProtoLog.d(WM_SHELL_TASK_ORG, "clear launch adjacent flag root container")
+ container?.let {
+ disableContainer(it)
+ container = null
+ }
+ }
+
+ private fun enableContainer(container: WindowContainerToken) {
+ KtProtoLog.v(WM_SHELL_TASK_ORG, "enable launch adjacent flag root container")
+ val wct = WindowContainerTransaction()
+ wct.setLaunchAdjacentFlagRoot(container)
+ syncQueue.queue(wct)
+ }
+
+ private fun disableContainer(container: WindowContainerToken) {
+ KtProtoLog.v(WM_SHELL_TASK_ORG, "disable launch adjacent flag root container")
+ val wct = WindowContainerTransaction()
+ wct.clearLaunchAdjacentFlagRoot(container)
+ syncQueue.queue(wct)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS
new file mode 100644
index 000000000000..7af038999797
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS
@@ -0,0 +1 @@
+madym@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
index e0c782d1675b..7c5bb211a4cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
@@ -14,16 +14,17 @@
* limitations under the License.
*/
-package com.android.wm.shell.common;
+package com.android.wm.shell.common.bubbles;
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import com.android.wm.shell.R;
+import androidx.annotation.DimenRes;
+import androidx.annotation.DrawableRes;
+import androidx.core.content.ContextCompat;
/**
* Circular view with a semitransparent, circular background with an 'X' inside it.
@@ -31,33 +32,44 @@ import com.android.wm.shell.R;
* This is used by both Bubbles and PIP as the dismiss target.
*/
public class DismissCircleView extends FrameLayout {
+ @DrawableRes int mBackgroundResId;
+ @DimenRes int mIconSizeResId;
private final ImageView mIconView = new ImageView(getContext());
public DismissCircleView(Context context) {
super(context);
- final Resources res = getResources();
-
- setBackground(res.getDrawable(R.drawable.dismiss_circle_background));
-
- mIconView.setImageDrawable(res.getDrawable(R.drawable.pip_ic_close_white));
addView(mIconView);
-
- setViewSizes();
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- final Resources res = getResources();
- setBackground(res.getDrawable(R.drawable.dismiss_circle_background));
+ setBackground(ContextCompat.getDrawable(getContext(), mBackgroundResId));
+ setViewSizes();
+ }
+
+ /**
+ * Sets up view with the provided resource ids.
+ * Decouples resource dependency in order to be used externally (e.g. Launcher)
+ *
+ * @param backgroundResId drawable resource id of the circle background
+ * @param iconResId drawable resource id of the icon for the dismiss view
+ * @param iconSizeResId dimen resource id of the icon size
+ */
+ public void setup(@DrawableRes int backgroundResId, @DrawableRes int iconResId,
+ @DimenRes int iconSizeResId) {
+ mBackgroundResId = backgroundResId;
+ mIconSizeResId = iconSizeResId;
+
+ setBackground(ContextCompat.getDrawable(getContext(), backgroundResId));
+ mIconView.setImageDrawable(ContextCompat.getDrawable(getContext(), iconResId));
setViewSizes();
}
/** Retrieves the current dimensions for the icon and circle and applies them. */
private void setViewSizes() {
- final Resources res = getResources();
- final int iconSize = res.getDimensionPixelSize(R.dimen.dismiss_target_x_size);
+ final int iconSize = getResources().getDimensionPixelSize(mIconSizeResId);
mIconView.setLayoutParams(
new FrameLayout.LayoutParams(iconSize, iconSize, Gravity.CENTER));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
index 67ecb915e098..d275a0be8e93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
@@ -14,41 +14,73 @@
* limitations under the License.
*/
-package com.android.wm.shell.bubbles
+package com.android.wm.shell.common.bubbles
import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.util.IntProperty
+import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowManager
import android.widget.FrameLayout
+import androidx.annotation.ColorRes
+import androidx.annotation.DimenRes
+import androidx.annotation.DrawableRes
+import androidx.core.content.ContextCompat
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
-import com.android.wm.shell.R
import com.android.wm.shell.animation.PhysicsAnimator
-import com.android.wm.shell.common.DismissCircleView
-/*
+/**
* View that handles interactions between DismissCircleView and BubbleStackView.
+ *
+ * @note [setup] method should be called after initialisation
*/
class DismissView(context: Context) : FrameLayout(context) {
+ /**
+ * The configuration is used to provide module specific resource ids
+ *
+ * @see [setup] method
+ */
+ data class Config(
+ /** dimen resource id of the dismiss target circle view size */
+ @DimenRes val targetSizeResId: Int,
+ /** dimen resource id of the icon size in the dismiss target */
+ @DimenRes val iconSizeResId: Int,
+ /** dimen resource id of the bottom margin for the dismiss target */
+ @DimenRes var bottomMarginResId: Int,
+ /** dimen resource id of the height for dismiss area gradient */
+ @DimenRes val floatingGradientHeightResId: Int,
+ /** color resource id of the dismiss area gradient color */
+ @ColorRes val floatingGradientColorResId: Int,
+ /** drawable resource id of the dismiss target background */
+ @DrawableRes val backgroundResId: Int,
+ /** drawable resource id of the icon for the dismiss target */
+ @DrawableRes val iconResId: Int
+ )
+
+ companion object {
+ private const val SHOULD_SETUP =
+ "The view isn't ready. Should be called after `setup`"
+ private val TAG = DismissView::class.simpleName
+ }
var circle = DismissCircleView(context)
var isShowing = false
- var targetSizeResId: Int
+ var config: Config? = null
private val animator = PhysicsAnimator.getInstance(circle)
private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
private val DISMISS_SCRIM_FADE_MS = 200L
private var wm: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
- private var gradientDrawable = createGradient()
+ private var gradientDrawable: GradientDrawable? = null
private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
object : IntProperty<GradientDrawable>("alpha") {
@@ -61,23 +93,41 @@ class DismissView(context: Context) : FrameLayout(context) {
}
init {
- setLayoutParams(LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
- Gravity.BOTTOM))
- updatePadding()
setClipToPadding(false)
setClipChildren(false)
setVisibility(View.INVISIBLE)
+ addView(circle)
+ }
+
+ /**
+ * Sets up view with the provided resource ids.
+ *
+ * Decouples resource dependency in order to be used externally (e.g. Launcher). Usually called
+ * with default params in module specific extension:
+ * @see [DismissView.setup] in DismissViewExt.kt
+ */
+ fun setup(config: Config) {
+ this.config = config
+
+ // Setup layout
+ layoutParams = LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ resources.getDimensionPixelSize(config.floatingGradientHeightResId),
+ Gravity.BOTTOM)
+ updatePadding()
+
+ // Setup gradient
+ gradientDrawable = createGradient(color = config.floatingGradientColorResId)
setBackgroundDrawable(gradientDrawable)
- targetSizeResId = R.dimen.dismiss_circle_size
- val targetSize: Int = resources.getDimensionPixelSize(targetSizeResId)
- addView(circle, LayoutParams(targetSize, targetSize,
- Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL))
- // start with circle offscreen so it's animated up
- circle.setTranslationY(resources.getDimensionPixelSize(
- R.dimen.floating_dismiss_gradient_height).toFloat())
+ // Setup DismissCircleView
+ circle.setup(config.backgroundResId, config.iconResId, config.iconSizeResId)
+ val targetSize: Int = resources.getDimensionPixelSize(config.targetSizeResId)
+ circle.layoutParams = LayoutParams(targetSize, targetSize,
+ Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
+ // Initial position with circle offscreen so it's animated up
+ circle.translationY = resources.getDimensionPixelSize(config.floatingGradientHeightResId)
+ .toFloat()
}
/**
@@ -85,6 +135,7 @@ class DismissView(context: Context) : FrameLayout(context) {
*/
fun show() {
if (isShowing) return
+ val gradientDrawable = checkExists(gradientDrawable) ?: return
isShowing = true
setVisibility(View.VISIBLE)
val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
@@ -104,6 +155,7 @@ class DismissView(context: Context) : FrameLayout(context) {
*/
fun hide() {
if (!isShowing) return
+ val gradientDrawable = checkExists(gradientDrawable) ?: return
isShowing = false
val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
gradientDrawable.alpha, 0)
@@ -124,18 +176,17 @@ class DismissView(context: Context) : FrameLayout(context) {
}
fun updateResources() {
+ val config = checkExists(config) ?: return
updatePadding()
- layoutParams.height = resources.getDimensionPixelSize(
- R.dimen.floating_dismiss_gradient_height)
-
- val targetSize = resources.getDimensionPixelSize(targetSizeResId)
+ layoutParams.height = resources.getDimensionPixelSize(config.floatingGradientHeightResId)
+ val targetSize = resources.getDimensionPixelSize(config.targetSizeResId)
circle.layoutParams.width = targetSize
circle.layoutParams.height = targetSize
circle.requestLayout()
}
- private fun createGradient(): GradientDrawable {
- val gradientColor = context.resources.getColor(android.R.color.system_neutral1_900)
+ private fun createGradient(@ColorRes color: Int): GradientDrawable {
+ val gradientColor = ContextCompat.getColor(context, color)
val alpha = 0.7f * 255
val gradientColorWithAlpha = Color.argb(alpha.toInt(),
Color.red(gradientColor),
@@ -150,10 +201,22 @@ class DismissView(context: Context) : FrameLayout(context) {
}
private fun updatePadding() {
+ val config = checkExists(config) ?: return
val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets()
val navInset = insets.getInsetsIgnoringVisibility(
WindowInsets.Type.navigationBars())
setPadding(0, 0, 0, navInset.bottom +
- resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
+ resources.getDimensionPixelSize(config.bottomMarginResId))
+ }
+
+ /**
+ * Checks if the value is set up and exists, if not logs an exception.
+ * Used for convenient logging in case `setup` wasn't called before
+ *
+ * @return value provided as argument
+ */
+ private fun <T>checkExists(value: T?): T? {
+ if (value == null) Log.e(TAG, SHOULD_SETUP)
+ return value
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
index ea9d065d5f53..cc37bd3a4589 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.bubbles
+package com.android.wm.shell.common.bubbles
import android.graphics.PointF
import android.view.MotionEvent
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
index c76937de6669..ec2680085fb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
@@ -76,6 +76,9 @@ public class DividerHandleView extends View {
private int mCurrentHeight;
private AnimatorSet mAnimator;
private boolean mTouching;
+ private boolean mHovering;
+ private final int mHoveringWidth;
+ private final int mHoveringHeight;
public DividerHandleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -87,6 +90,8 @@ public class DividerHandleView extends View {
mCurrentHeight = mHeight;
mTouchingWidth = mWidth > mHeight ? mWidth / 2 : mWidth;
mTouchingHeight = mHeight > mWidth ? mHeight / 2 : mHeight;
+ mHoveringWidth = mWidth > mHeight ? ((int) (mWidth * 1.5f)) : mWidth;
+ mHoveringHeight = mHeight > mWidth ? ((int) (mHeight * 1.5f)) : mHeight;
}
/** Sets touching state for this handle view. */
@@ -94,24 +99,32 @@ public class DividerHandleView extends View {
if (touching == mTouching) {
return;
}
+ setInputState(touching, animate, mTouchingWidth, mTouchingHeight);
+ mTouching = touching;
+ }
+
+ /** Sets hovering state for this handle view. */
+ public void setHovering(boolean hovering, boolean animate) {
+ if (hovering == mHovering) {
+ return;
+ }
+ setInputState(hovering, animate, mHoveringWidth, mHoveringHeight);
+ mHovering = hovering;
+ }
+
+ private void setInputState(boolean stateOn, boolean animate, int stateWidth, int stateHeight) {
if (mAnimator != null) {
mAnimator.cancel();
mAnimator = null;
}
if (!animate) {
- if (touching) {
- mCurrentWidth = mTouchingWidth;
- mCurrentHeight = mTouchingHeight;
- } else {
- mCurrentWidth = mWidth;
- mCurrentHeight = mHeight;
- }
+ mCurrentWidth = stateOn ? stateWidth : mWidth;
+ mCurrentHeight = stateOn ? stateHeight : mHeight;
invalidate();
} else {
- animateToTarget(touching ? mTouchingWidth : mWidth,
- touching ? mTouchingHeight : mHeight, touching);
+ animateToTarget(stateOn ? stateWidth : mWidth,
+ stateOn ? stateHeight : mHeight, stateOn);
}
- mTouching = touching;
}
private void animateToTarget(int targetWidth, int targetHeight, boolean touching) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 69f0bad4fb45..262d487b1d1b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -17,14 +17,19 @@
package com.android.wm.shell.common.split;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CURSOR_HOVER_STATES_ENABLED;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
+import android.provider.DeviceConfig;
import android.util.AttributeSet;
import android.util.Property;
import android.view.GestureDetector;
@@ -32,6 +37,7 @@ import android.view.InsetsController;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.MotionEvent;
+import android.view.PointerIcon;
import android.view.SurfaceControlViewHost;
import android.view.VelocityTracker;
import android.view.View;
@@ -46,6 +52,7 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
@@ -222,7 +229,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
for (int i = insetsState.sourceSize() - 1; i >= 0; i--) {
final InsetsSource source = insetsState.sourceAt(i);
if (source.getType() == WindowInsets.Type.navigationBars()
- && source.insetsRoundedCornerFrame()) {
+ && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) {
mTempRect.inset(source.calculateVisibleInsets(mTempRect));
}
}
@@ -270,6 +277,12 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
}
@Override
+ public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
+ return PointerIcon.getSystemIcon(getContext(),
+ isLandscape() ? TYPE_HORIZONTAL_DOUBLE_ARROW : TYPE_VERTICAL_DOUBLE_ARROW);
+ }
+
+ @Override
public boolean onTouch(View v, MotionEvent event) {
if (mSplitLayout == null || !mInteractive) {
return false;
@@ -371,6 +384,43 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
mViewHost.relayout(lp);
}
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, CURSOR_HOVER_STATES_ENABLED,
+ /* defaultValue = */ false)) {
+ return false;
+ }
+
+ if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
+ setHovering();
+ return true;
+ } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
+ releaseHovering();
+ return true;
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ void setHovering() {
+ mHandle.setHovering(true, true);
+ mHandle.animate()
+ .setInterpolator(Interpolators.TOUCH_RESPONSE)
+ .setDuration(TOUCH_ANIMATION_DURATION)
+ .translationZ(mTouchElevation)
+ .start();
+ }
+
+ @VisibleForTesting
+ void releaseHovering() {
+ mHandle.setHovering(false, true);
+ mHandle.animate()
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
+ .translationZ(0)
+ .start();
+ }
+
/**
* Set divider should interactive to user or not.
*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index 12d51f54a09c..47d58afb6aba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -25,6 +25,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -86,13 +87,14 @@ public class TvWMShellModule {
TransactionPool transactionPool,
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
+ LaunchAdjacentController launchAdjacentController,
@ShellMainThread ShellExecutor mainExecutor,
Handler mainHandler,
SystemWindows systemWindows) {
return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
displayImeController, displayInsetsController, dragAndDropController, transitions,
- transactionPool, iconProvider, recentTasks, mainExecutor, mainHandler,
- systemWindows);
+ transactionPool, iconProvider, recentTasks, launchAdjacentController, mainExecutor,
+ mainHandler, systemWindows);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index c491fed5d896..34a6e0ae406c 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
@@ -46,6 +46,7 @@ import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.DockStateReader;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -275,6 +276,13 @@ public abstract class WMShellBaseModule {
return new WindowManagerShellWrapper(mainExecutor);
}
+ @WMSingleton
+ @Provides
+ static LaunchAdjacentController provideLaunchAdjacentController(
+ SyncTransactionQueue syncQueue) {
+ return new LaunchAdjacentController(syncQueue);
+ }
+
//
// Back animation
//
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 cff317259f1e..20c3bd2dccc4 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
@@ -36,17 +36,21 @@ import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.bubbles.properties.ProdBubbleProperties;
+import com.android.wm.shell.bubbles.storage.BubblePersistentRepository;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.desktopmode.DesktopModeController;
@@ -55,6 +59,7 @@ import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
@@ -180,11 +185,12 @@ public abstract class WMShellModule {
IWindowManager wmService) {
return new BubbleController(context, shellInit, shellCommandHandler, shellController, data,
null /* synchronizer */, floatingContentCoordinator,
- new BubbleDataRepository(context, launcherApps, mainExecutor),
+ new BubbleDataRepository(launcherApps, mainExecutor,
+ new BubblePersistentRepository(context)),
statusBarService, windowManager, windowManagerShellWrapper, userManager,
launcherApps, logger, taskStackListener, organizer, positioner, displayController,
oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor,
- taskViewTransitions, syncQueue, wmService);
+ taskViewTransitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE);
}
//
@@ -202,8 +208,7 @@ public abstract class WMShellModule {
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopModeController> desktopModeController,
- Optional<DesktopTasksController> desktopTasksController,
- Optional<SplitScreenController> splitScreenController) {
+ Optional<DesktopTasksController> desktopTasksController) {
if (DesktopModeStatus.isAnyEnabled()) {
return new DesktopModeWindowDecorViewModel(
context,
@@ -214,16 +219,15 @@ public abstract class WMShellModule {
syncQueue,
transitions,
desktopModeController,
- desktopTasksController,
- splitScreenController);
+ desktopTasksController);
}
return new CaptionWindowDecorViewModel(
- context,
- mainHandler,
- mainChoreographer,
- taskOrganizer,
- displayController,
- syncQueue);
+ context,
+ mainHandler,
+ mainChoreographer,
+ taskOrganizer,
+ displayController,
+ syncQueue);
}
//
@@ -263,8 +267,13 @@ public abstract class WMShellModule {
static FreeformTaskTransitionHandler provideFreeformTaskTransitionHandler(
ShellInit shellInit,
Transitions transitions,
- WindowDecorViewModel windowDecorViewModel) {
- return new FreeformTaskTransitionHandler(shellInit, transitions, windowDecorViewModel);
+ Context context,
+ WindowDecorViewModel windowDecorViewModel,
+ DisplayController displayController,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellAnimationThread ShellExecutor animExecutor) {
+ return new FreeformTaskTransitionHandler(shellInit, transitions, context,
+ windowDecorViewModel, displayController, mainExecutor, animExecutor);
}
@WMSingleton
@@ -327,11 +336,14 @@ public abstract class WMShellModule {
TransactionPool transactionPool,
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
+ LaunchAdjacentController launchAdjacentController,
+ Optional<WindowDecorViewModel> windowDecorViewModel,
@ShellMainThread ShellExecutor mainExecutor) {
return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
displayImeController, displayInsetsController, dragAndDropController, transitions,
- transactionPool, iconProvider, recentTasks, mainExecutor);
+ transactionPool, iconProvider, recentTasks, launchAdjacentController,
+ windowDecorViewModel, mainExecutor);
}
//
@@ -535,11 +547,13 @@ public abstract class WMShellModule {
Optional<PipTouchHandler> pipTouchHandlerOptional,
Optional<RecentsTransitionHandler> recentsTransitionHandler,
KeyguardTransitionHandler keyguardTransitionHandler,
+ Optional<DesktopModeController> desktopModeController,
+ Optional<DesktopTasksController> desktopTasksController,
Optional<UnfoldTransitionHandler> unfoldHandler,
Transitions transitions) {
return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
pipTouchHandlerOptional, recentsTransitionHandler, keyguardTransitionHandler,
- unfoldHandler);
+ desktopModeController, desktopTasksController, unfoldHandler);
}
@WMSingleton
@@ -573,13 +587,13 @@ public abstract class WMShellModule {
animators.add(fullscreenAnimator);
return new UnfoldAnimationController(
- shellInit,
- transactionPool,
- progressProvider.get(),
- animators,
- unfoldTransitionHandler,
- mainExecutor
- );
+ shellInit,
+ transactionPool,
+ progressProvider.get(),
+ animators,
+ unfoldTransitionHandler,
+ mainExecutor
+ );
}
@Provides
@@ -671,6 +685,7 @@ public abstract class WMShellModule {
static DesktopTasksController provideDesktopTasksController(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
ShellTaskOrganizer shellTaskOrganizer,
@@ -679,13 +694,16 @@ public abstract class WMShellModule {
Transitions transitions,
EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
+ ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
+ LaunchAdjacentController launchAdjacentController,
@ShellMainThread ShellExecutor mainExecutor
) {
- return new DesktopTasksController(context, shellInit, shellController, displayController,
- shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, transitions,
- enterDesktopTransitionHandler, exitDesktopTransitionHandler,
- desktopModeTaskRepository, mainExecutor);
+ return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
+ displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
+ transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
+ toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository,
+ launchAdjacentController, mainExecutor);
}
@WMSingleton
@@ -697,6 +715,13 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
+ static ToggleResizeDesktopTaskTransitionHandler provideToggleResizeDesktopTaskTransitionHandler(
+ Transitions transitions) {
+ return new ToggleResizeDesktopTaskTransitionHandler(transitions);
+ }
+
+ @WMSingleton
+ @Provides
static ExitDesktopTaskTransitionHandler provideExitDesktopTaskTransitionHandler(
Transitions transitions,
Context context
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index b9d2be280efb..db6c258e84c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -33,6 +33,7 @@ import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DE
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
import android.content.Context;
+import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Region;
import android.net.Uri;
@@ -414,6 +415,25 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
}
/**
+ * Applies the proper surface states (rounded corners) to tasks when desktop mode is active.
+ * This is intended to be used when desktop mode is part of another animation but isn't, itself,
+ * animating.
+ */
+ public void syncSurfaceState(@NonNull TransitionInfo info,
+ SurfaceControl.Transaction finishTransaction) {
+ // Add rounded corners to freeform windows
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[]{android.R.attr.dialogCornerRadius});
+ final int cornerRadius = ta.getDimensionPixelSize(0, 0);
+ ta.recycle();
+ for (TransitionInfo.Change change: info.getChanges()) {
+ if (change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ finishTransaction.setCornerRadius(change.getLeash(), cornerRadius);
+ }
+ }
+ }
+
+ /**
* A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE}
*/
private final class SettingsObserver extends ContentObserver {
@@ -500,6 +520,11 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
}
@Override
+ public void showDesktopApp(int taskId) throws RemoteException {
+ // TODO
+ }
+
+ @Override
public int getVisibleTaskCount(int displayId) throws RemoteException {
int[] result = new int[1];
executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount",
@@ -508,5 +533,20 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
);
return result[0];
}
+
+ @Override
+ public void stashDesktopApps(int displayId) throws RemoteException {
+ // Stashing of desktop apps not needed. Apps always launch on desktop
+ }
+
+ @Override
+ public void hideStashedDesktopApps(int displayId) throws RemoteException {
+ // Stashing of desktop apps not needed. Apps always launch on desktop
+ }
+
+ @Override
+ public void setTaskListener(IDesktopTaskListener listener) throws RemoteException {
+ // TODO(b/261234402): move visibility from sysui state to listener
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 3ab175d3b68a..711df0d89936 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -25,6 +25,7 @@ import androidx.core.util.keyIterator
import androidx.core.util.valueIterator
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.util.KtProtoLog
+import java.io.PrintWriter
import java.util.concurrent.Executor
import java.util.function.Consumer
@@ -43,6 +44,7 @@ class DesktopModeTaskRepository {
*/
val activeTasks: ArraySet<Int> = ArraySet(),
val visibleTasks: ArraySet<Int> = ArraySet(),
+ var stashed: Boolean = false
)
// Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
@@ -85,8 +87,10 @@ class DesktopModeTaskRepository {
visibleTasksListeners[visibleTasksListener] = executor
displayData.keyIterator().forEach { displayId ->
val visibleTasks = getVisibleTaskCount(displayId)
+ val stashed = isStashed(displayId)
executor.execute {
visibleTasksListener.onVisibilityChanged(displayId, visibleTasks > 0)
+ visibleTasksListener.onStashedChanged(displayId, stashed)
}
}
}
@@ -312,6 +316,52 @@ class DesktopModeTaskRepository {
}
/**
+ * Update stashed status on display with id [displayId]
+ */
+ fun setStashed(displayId: Int, stashed: Boolean) {
+ val data = displayData.getOrCreate(displayId)
+ val oldValue = data.stashed
+ data.stashed = stashed
+ if (oldValue != stashed) {
+ KtProtoLog.d(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTaskRepo: mark stashed=%b displayId=%d",
+ stashed,
+ displayId
+ )
+ visibleTasksListeners.forEach { (listener, executor) ->
+ executor.execute { listener.onStashedChanged(displayId, stashed) }
+ }
+ }
+ }
+
+ /**
+ * Check if display with id [displayId] has desktop tasks stashed
+ */
+ fun isStashed(displayId: Int): Boolean {
+ return displayData[displayId]?.stashed ?: false
+ }
+
+ internal fun dump(pw: PrintWriter, prefix: String) {
+ val innerPrefix = "$prefix "
+ pw.println("${prefix}DesktopModeTaskRepository")
+ dumpDisplayData(pw, innerPrefix)
+ pw.println("${innerPrefix}freeformTasksInZOrder=${freeformTasksInZOrder.toDumpString()}")
+ pw.println("${innerPrefix}activeTasksListeners=${activeTasksListeners.size}")
+ pw.println("${innerPrefix}visibleTasksListeners=${visibleTasksListeners.size}")
+ }
+
+ private fun dumpDisplayData(pw: PrintWriter, prefix: String) {
+ val innerPrefix = "$prefix "
+ displayData.forEach { displayId, data ->
+ pw.println("${prefix}Display $displayId:")
+ pw.println("${innerPrefix}activeTasks=${data.activeTasks.toDumpString()}")
+ pw.println("${innerPrefix}visibleTasks=${data.visibleTasks.toDumpString()}")
+ pw.println("${innerPrefix}stashed=${data.stashed}")
+ }
+ }
+
+ /**
* Defines interface for classes that can listen to changes for active tasks in desktop mode.
*/
interface ActiveTasksListener {
@@ -331,5 +381,15 @@ class DesktopModeTaskRepository {
*/
@JvmDefault
fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {}
+
+ /**
+ * Called when the desktop stashed status changes.
+ */
+ @JvmDefault
+ fun onStashedChanged(displayId: Int, stashed: Boolean) {}
}
}
+
+private fun <T> Iterable<T>.toDumpString(): String {
+ return joinToString(separator = ", ", prefix = "[", postfix = "]")
+} \ No newline at end of file
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 91bb155d9d01..4fda4b7896c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.desktopmode
+import android.R
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
@@ -24,19 +25,21 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
+import android.content.res.TypedArray
import android.graphics.Point
import android.graphics.Rect
import android.graphics.Region
import android.os.IBinder
import android.os.SystemProperties
+import android.util.DisplayMetrics.DENSITY_DEFAULT
import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
-import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import androidx.annotation.BinderThread
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -44,18 +47,23 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ExecutorUtils
import com.android.wm.shell.common.ExternalInterfaceBinder
+import com.android.wm.shell.common.LaunchAdjacentController
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.common.SyncTransactionQueue
import com.android.wm.shell.common.annotations.ExternalThread
import com.android.wm.shell.common.annotations.ShellMainThread
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.sysui.ShellSharedConstants
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.util.KtProtoLog
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import java.io.PrintWriter
import java.util.concurrent.Executor
import java.util.function.Consumer
@@ -63,6 +71,7 @@ import java.util.function.Consumer
class DesktopTasksController(
private val context: Context,
shellInit: ShellInit,
+ private val shellCommandHandler: ShellCommandHandler,
private val shellController: ShellController,
private val displayController: DisplayController,
private val shellTaskOrganizer: ShellTaskOrganizer,
@@ -71,7 +80,10 @@ class DesktopTasksController(
private val transitions: Transitions,
private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
+ private val toggleResizeDesktopTaskTransitionHandler:
+ ToggleResizeDesktopTaskTransitionHandler,
private val desktopModeTaskRepository: DesktopModeTaskRepository,
+ private val launchAdjacentController: LaunchAdjacentController,
@ShellMainThread private val mainExecutor: ShellExecutor
) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler {
@@ -82,6 +94,15 @@ class DesktopTasksController(
visualIndicator?.releaseVisualIndicator(t)
visualIndicator = null
}
+ private val taskVisibilityListener = object : VisibleTasksListener {
+ override fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {
+ launchAdjacentController.launchAdjacentEnabled = !hasVisibleFreeformTasks
+ }
+ }
+
+ private val transitionAreaHeight
+ get() = context.resources.getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_height)
init {
desktopMode = DesktopModeImpl()
@@ -92,12 +113,14 @@ class DesktopTasksController(
private fun onInit() {
KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopTasksController")
+ shellCommandHandler.addDumpCallback(this::dump, this)
shellController.addExternalInterface(
ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE,
{ createExternalInterface() },
this
)
transitions.addHandler(this)
+ desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor)
}
/** Show all tasks, that are part of the desktop, on top of launcher */
@@ -118,27 +141,58 @@ class DesktopTasksController(
}
}
+ /**
+ * Stash desktop tasks on display with id [displayId].
+ *
+ * When desktop tasks are stashed, launcher home screen icons are fully visible. New apps
+ * launched in this state will be added to the desktop. Existing desktop tasks will be brought
+ * back to front during the launch.
+ */
+ fun stashDesktopApps(displayId: Int) {
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: stashDesktopApps")
+ desktopModeTaskRepository.setStashed(displayId, true)
+ }
+
+ /**
+ * Clear the stashed state for the given display
+ */
+ fun hideStashedDesktopApps(displayId: Int) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: hideStashedApps displayId=%d",
+ displayId
+ )
+ desktopModeTaskRepository.setStashed(displayId, false)
+ }
+
/** Get number of tasks that are marked as visible */
fun getVisibleTaskCount(displayId: Int): Int {
return desktopModeTaskRepository.getVisibleTaskCount(displayId)
}
/** Move a task with given `taskId` to desktop */
- fun moveToDesktop(taskId: Int) {
- shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToDesktop(task) }
+ fun moveToDesktop(taskId: Int, wct: WindowContainerTransaction = WindowContainerTransaction()) {
+ shellTaskOrganizer.getRunningTaskInfo(taskId)?.let {
+ task -> moveToDesktop(task, wct)
+ }
}
- /** Move a task to desktop */
- fun moveToDesktop(task: RunningTaskInfo) {
+ /**
+ * Move a task to desktop
+ */
+ fun moveToDesktop(
+ task: RunningTaskInfo,
+ wct: WindowContainerTransaction = WindowContainerTransaction()
+ ) {
KtProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: moveToDesktop taskId=%d",
task.taskId
)
- val wct = WindowContainerTransaction()
// Bring other apps to front first
bringDesktopAppsToFront(task.displayId, wct)
- addMoveToDesktopChanges(wct, task.token)
+ addMoveToDesktopChanges(wct, task)
+
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
} else {
@@ -158,7 +212,7 @@ class DesktopTasksController(
)
val wct = WindowContainerTransaction()
moveHomeTaskToFront(wct)
- addMoveToDesktopChanges(wct, taskInfo.getToken())
+ addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, startBounds)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -178,7 +232,7 @@ class DesktopTasksController(
)
val wct = WindowContainerTransaction()
bringDesktopAppsToFront(taskInfo.displayId, wct)
- addMoveToDesktopChanges(wct, taskInfo.getToken())
+ addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, freeformBounds)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -204,7 +258,7 @@ class DesktopTasksController(
)
val wct = WindowContainerTransaction()
- addMoveToFullscreenChanges(wct, task.token)
+ addMoveToFullscreenChanges(wct, task)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
} else {
@@ -218,16 +272,24 @@ class DesktopTasksController(
*/
fun cancelMoveToFreeform(task: RunningTaskInfo, position: Point) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: cancelMoveToFreeform taskId=%d",
- task.taskId
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: cancelMoveToFreeform taskId=%d",
+ task.taskId
)
val wct = WindowContainerTransaction()
- addMoveToFullscreenChanges(wct, task.token)
+ wct.setBounds(task.token, null)
+
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, position,
- mOnAnimationFinishedCallback)
+ enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(
+ wct, position) { t ->
+ val callbackWCT = WindowContainerTransaction()
+ visualIndicator?.releaseVisualIndicator(t)
+ visualIndicator = null
+ addMoveToFullscreenChanges(callbackWCT, task)
+ shellTaskOrganizer.applyTransaction(callbackWCT)
+ }
} else {
+ addMoveToFullscreenChanges(wct, task)
shellTaskOrganizer.applyTransaction(wct)
releaseVisualIndicator()
}
@@ -240,7 +302,7 @@ class DesktopTasksController(
task.taskId
)
val wct = WindowContainerTransaction()
- addMoveToFullscreenChanges(wct, task.token)
+ addMoveToFullscreenChanges(wct, task)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
exitDesktopTaskTransitionHandler.startTransition(
@@ -252,6 +314,11 @@ class DesktopTasksController(
}
/** Move a task to the front */
+ fun moveTaskToFront(taskId: Int) {
+ shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveTaskToFront(task) }
+ }
+
+ /** Move a task to the front */
fun moveTaskToFront(taskInfo: RunningTaskInfo) {
KtProtoLog.v(
WM_SHELL_DESKTOP_MODE,
@@ -331,6 +398,49 @@ class DesktopTasksController(
}
}
+ /** Quick-resizes a desktop task, toggling between the stable bounds and the default bounds. */
+ fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo, windowDecor: DesktopModeWindowDecoration) {
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
+
+ val stableBounds = Rect()
+ displayLayout.getStableBounds(stableBounds)
+ val destinationBounds = Rect()
+ if (taskInfo.configuration.windowConfiguration.bounds == stableBounds) {
+ // The desktop task is currently occupying the whole stable bounds, toggle to the
+ // default bounds.
+ getDefaultDesktopTaskBounds(
+ density = taskInfo.configuration.densityDpi.toFloat() / DENSITY_DEFAULT,
+ stableBounds = stableBounds,
+ outBounds = destinationBounds
+ )
+ } else {
+ // Toggle to the stable bounds.
+ destinationBounds.set(stableBounds)
+ }
+
+ val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ toggleResizeDesktopTaskTransitionHandler.startTransition(
+ wct,
+ taskInfo.taskId,
+ windowDecor
+ )
+ } else {
+ shellTaskOrganizer.applyTransaction(wct)
+ }
+ }
+
+ private fun getDefaultDesktopTaskBounds(density: Float, stableBounds: Rect, outBounds: Rect) {
+ val width = (DESKTOP_MODE_DEFAULT_WIDTH_DP * density + 0.5f).toInt()
+ val height = (DESKTOP_MODE_DEFAULT_HEIGHT_DP * density + 0.5f).toInt()
+ outBounds.set(0, 0, width, height)
+ // Center the task in stable bounds
+ outBounds.offset(
+ stableBounds.centerX() - outBounds.centerX(),
+ stableBounds.centerY() - outBounds.centerY()
+ )
+ }
+
/**
* Get windowing move for a given `taskId`
*
@@ -397,83 +507,169 @@ class DesktopTasksController(
transition: IBinder,
request: TransitionRequestInfo
): WindowContainerTransaction? {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: handleRequest request=%s",
+ request
+ )
// Check if we should skip handling this transition
+ var reason = ""
val shouldHandleRequest =
when {
// Only handle open or to front transitions
- request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false
+ request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> {
+ reason = "transition type not handled (${request.type})"
+ false
+ }
// Only handle when it is a task transition
- request.triggerTask == null -> false
+ request.triggerTask == null -> {
+ reason = "triggerTask is null"
+ false
+ }
// Only handle standard type tasks
- request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> false
+ request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> {
+ reason = "activityType not handled (${request.triggerTask.activityType})"
+ false
+ }
// Only handle fullscreen or freeform tasks
request.triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN &&
- request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> false
+ request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> {
+ reason = "windowingMode not handled (${request.triggerTask.windowingMode})"
+ false
+ }
// Otherwise process it
else -> true
}
if (!shouldHandleRequest) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: skipping handleRequest reason=%s",
+ reason
+ )
return null
}
val task: RunningTaskInfo = request.triggerTask
- val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
- // Check if we should switch a fullscreen task to freeform
- if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) {
- // If there are any visible desktop tasks, switch the task to freeform
- if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
- KtProtoLog.d(
+ val result = when {
+ // If display has tasks stashed, handle as stashed launch
+ desktopModeTaskRepository.isStashed(task.displayId) -> handleStashedTaskLaunch(task)
+ // Check if fullscreen task should be updated
+ task.windowingMode == WINDOWING_MODE_FULLSCREEN -> handleFullscreenTaskLaunch(task)
+ // Check if freeform task should be updated
+ task.windowingMode == WINDOWING_MODE_FREEFORM -> handleFreeformTaskLaunch(task)
+ else -> {
+ null
+ }
+ }
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: handleRequest result=%s",
+ result ?: "null"
+ )
+ return result
+ }
+
+ /**
+ * Applies the proper surface states (rounded corners) to tasks when desktop mode is active.
+ * This is intended to be used when desktop mode is part of another animation but isn't, itself,
+ * animating.
+ */
+ fun syncSurfaceState(
+ info: TransitionInfo,
+ finishTransaction: SurfaceControl.Transaction
+ ) {
+ // Add rounded corners to freeform windows
+ val ta: TypedArray = context.obtainStyledAttributes(
+ intArrayOf(R.attr.dialogCornerRadius))
+ val cornerRadius = ta.getDimensionPixelSize(0, 0).toFloat()
+ ta.recycle()
+ info.changes
+ .filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
+ .forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
+ }
+
+ private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch")
+ val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
+ if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) {
+ KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: switch fullscreen task to freeform on transition" +
- " taskId=%d",
+ "DesktopTasksController: switch freeform task to fullscreen oon transition" +
+ " taskId=%d",
task.taskId
- )
- return WindowContainerTransaction().also { wct ->
- addMoveToDesktopChanges(wct, task.token)
- }
+ )
+ return WindowContainerTransaction().also { wct ->
+ addMoveToFullscreenChanges(wct, task)
}
}
+ return null
+ }
- // CHeck if we should switch a freeform task to fullscreen
- if (task.windowingMode == WINDOWING_MODE_FREEFORM) {
- // If no visible desktop tasks, switch this task to freeform as the transition came
- // outside of this controller
- if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) {
- KtProtoLog.d(
+ private fun handleFullscreenTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFullscreenTaskLaunch")
+ val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
+ if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
+ KtProtoLog.d(
WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: switch freeform task to fullscreen oon transition" +
- " taskId=%d",
+ "DesktopTasksController: switch fullscreen task to freeform on transition" +
+ " taskId=%d",
task.taskId
- )
- return WindowContainerTransaction().also { wct ->
- addMoveToFullscreenChanges(wct, task.token)
- }
+ )
+ return WindowContainerTransaction().also { wct ->
+ addMoveToDesktopChanges(wct, task)
}
}
return null
}
+ private fun handleStashedTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction {
+ KtProtoLog.d(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: launch apps with stashed on transition taskId=%d",
+ task.taskId
+ )
+ val wct = WindowContainerTransaction()
+ bringDesktopAppsToFront(task.displayId, wct)
+ addMoveToDesktopChanges(wct, task)
+ desktopModeTaskRepository.setStashed(task.displayId, false)
+ return wct
+ }
+
private fun addMoveToDesktopChanges(
wct: WindowContainerTransaction,
- token: WindowContainerToken
+ taskInfo: RunningTaskInfo
) {
- wct.setWindowingMode(token, WINDOWING_MODE_FREEFORM)
- wct.reorder(token, true /* onTop */)
+ val displayWindowingMode = taskInfo.configuration.windowConfiguration.displayWindowingMode
+ val targetWindowingMode = if (displayWindowingMode == WINDOWING_MODE_FREEFORM) {
+ // Display windowing is freeform, set to undefined and inherit it
+ WINDOWING_MODE_UNDEFINED
+ } else {
+ WINDOWING_MODE_FREEFORM
+ }
+ wct.setWindowingMode(taskInfo.token, targetWindowingMode)
+ wct.reorder(taskInfo.token, true /* onTop */)
if (isDesktopDensityOverrideSet()) {
- wct.setDensityDpi(token, getDesktopDensityDpi())
+ wct.setDensityDpi(taskInfo.token, getDesktopDensityDpi())
}
}
private fun addMoveToFullscreenChanges(
wct: WindowContainerTransaction,
- token: WindowContainerToken
+ taskInfo: RunningTaskInfo
) {
- wct.setWindowingMode(token, WINDOWING_MODE_FULLSCREEN)
- wct.setBounds(token, null)
+ val displayWindowingMode = taskInfo.configuration.windowConfiguration.displayWindowingMode
+ val targetWindowingMode = if (displayWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ // Display windowing is fullscreen, set to undefined and inherit it
+ WINDOWING_MODE_UNDEFINED
+ } else {
+ WINDOWING_MODE_FULLSCREEN
+ }
+ wct.setWindowingMode(taskInfo.token, targetWindowingMode)
+ wct.setBounds(taskInfo.token, null)
if (isDesktopDensityOverrideSet()) {
- wct.setDensityDpi(token, getFullscreenDensityDpi())
+ wct.setDensityDpi(taskInfo.token, getFullscreenDensityDpi())
}
}
@@ -508,13 +704,12 @@ class DesktopTasksController(
y: Float
) {
if (taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
- val statusBarHeight = getStatusBarHeight(taskInfo)
- if (y <= statusBarHeight && visualIndicator == null) {
+ if (y <= transitionAreaHeight && visualIndicator == null) {
visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
displayController, context, taskSurface, shellTaskOrganizer,
rootTaskDisplayAreaOrganizer)
visualIndicator?.createFullscreenIndicatorWithAnimatedBounds()
- } else if (y > statusBarHeight && visualIndicator != null) {
+ } else if (y > transitionAreaHeight && visualIndicator != null) {
releaseVisualIndicator()
}
}
@@ -524,14 +719,18 @@ class DesktopTasksController(
* Perform checks required on drag end. Move to fullscreen if drag ends in status bar area.
*
* @param taskInfo the task being dragged.
- * @param position position of surface when drag ends
+ * @param position position of surface when drag ends.
+ * @param y the Y position of the motion event.
+ * @param windowDecor the window decoration for the task being dragged
*/
fun onDragPositioningEnd(
taskInfo: RunningTaskInfo,
- position: Point
+ position: Point,
+ y: Float,
+ windowDecor: DesktopModeWindowDecoration
) {
- val statusBarHeight = getStatusBarHeight(taskInfo)
- if (position.y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
+ if (y <= transitionAreaHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
+ windowDecor.incrementRelayoutBlock()
moveToFullscreenWithAnimation(taskInfo, position)
}
}
@@ -549,9 +748,9 @@ class DesktopTasksController(
taskSurface: SurfaceControl,
y: Float
) {
- // If the motion event is above the status bar, return since we do not need to show the
- // visual indicator at this point.
- if (y < getStatusBarHeight(taskInfo)) {
+ // If the motion event is above the status bar and the visual indicator is not yet visible,
+ // return since we do not need to show the visual indicator at this point.
+ if (y < getStatusBarHeight(taskInfo) && visualIndicator == null) {
return
}
if (visualIndicator == null) {
@@ -632,6 +831,12 @@ class DesktopTasksController(
desktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor)
}
+ private fun dump(pw: PrintWriter, prefix: String) {
+ val innerPrefix = "$prefix "
+ pw.println("${prefix}DesktopTasksController")
+ desktopModeTaskRepository.dump(pw, innerPrefix)
+ }
+
/** The interface for calls from outside the shell, within the host process. */
@ExternalThread
private inner class DesktopModeImpl : DesktopMode {
@@ -658,8 +863,51 @@ class DesktopTasksController(
@BinderThread
private class IDesktopModeImpl(private var controller: DesktopTasksController?) :
IDesktopMode.Stub(), ExternalInterfaceBinder {
+
+ private lateinit var remoteListener:
+ SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>
+
+ private val listener: VisibleTasksListener = object : VisibleTasksListener {
+ override fun onVisibilityChanged(displayId: Int, visible: Boolean) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: onVisibilityChanged display=%d visible=%b",
+ displayId,
+ visible
+ )
+ remoteListener.call { l -> l.onVisibilityChanged(displayId, visible) }
+ }
+
+ override fun onStashedChanged(displayId: Int, stashed: Boolean) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: onStashedChanged display=%d stashed=%b",
+ displayId,
+ stashed
+ )
+ remoteListener.call { l -> l.onStashedChanged(displayId, stashed) }
+ }
+ }
+
+ init {
+ remoteListener =
+ SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>(
+ controller,
+ { c ->
+ c.desktopModeTaskRepository.addVisibleTasksListener(
+ listener,
+ c.mainExecutor
+ )
+ },
+ { c ->
+ c.desktopModeTaskRepository.removeVisibleTasksListener(listener)
+ }
+ )
+ }
+
/** Invalidates this instance, preventing future calls from updating the controller. */
override fun invalidate() {
+ remoteListener.unregister()
controller = null
}
@@ -670,6 +918,27 @@ class DesktopTasksController(
) { c -> c.showDesktopApps(displayId) }
}
+ override fun stashDesktopApps(displayId: Int) {
+ ExecutorUtils.executeRemoteCallWithTaskPermission(
+ controller,
+ "stashDesktopApps"
+ ) { c -> c.stashDesktopApps(displayId) }
+ }
+
+ override fun hideStashedDesktopApps(displayId: Int) {
+ ExecutorUtils.executeRemoteCallWithTaskPermission(
+ controller,
+ "hideStashedDesktopApps"
+ ) { c -> c.hideStashedDesktopApps(displayId) }
+ }
+
+ override fun showDesktopApp(taskId: Int) {
+ ExecutorUtils.executeRemoteCallWithTaskPermission(
+ controller,
+ "showDesktopApp"
+ ) { c -> c.moveTaskToFront(taskId) }
+ }
+
override fun getVisibleTaskCount(displayId: Int): Int {
val result = IntArray(1)
ExecutorUtils.executeRemoteCallWithTaskPermission(
@@ -680,13 +949,33 @@ class DesktopTasksController(
)
return result[0]
}
+
+ override fun setTaskListener(listener: IDesktopTaskListener?) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: set task listener=%s",
+ listener ?: "null"
+ )
+ ExecutorUtils.executeRemoteCallWithTaskPermission(
+ controller,
+ "setTaskListener"
+ ) { _ -> listener?.let { remoteListener.register(it) } ?: remoteListener.unregister() }
+ }
}
companion object {
private val DESKTOP_DENSITY_OVERRIDE =
- SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 0)
+ SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 284)
private val DESKTOP_DENSITY_ALLOWED_RANGE = (100..1000)
+ // Override default freeform task width when desktop mode is enabled. In dips.
+ private val DESKTOP_MODE_DEFAULT_WIDTH_DP =
+ SystemProperties.getInt("persist.wm.debug.desktop_mode.default_width", 840)
+
+ // Override default freeform task height when desktop mode is enabled. In dips.
+ private val DESKTOP_MODE_DEFAULT_HEIGHT_DP =
+ SystemProperties.getInt("persist.wm.debug.desktop_mode.default_height", 630)
+
/**
* Check if desktop density override is enabled
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 3733b919e366..3e175f3a9ae2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -17,7 +17,6 @@
package com.android.wm.shell.desktopmode;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -200,7 +199,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
}
if (type == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
&& mPosition != null) {
// This Transition animates a task to fullscreen after being dragged from the status
// bar and then released back into the status bar area
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 899d67267e69..ee3a080e7318 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -16,6 +16,8 @@
package com.android.wm.shell.desktopmode;
+import com.android.wm.shell.desktopmode.IDesktopTaskListener;
+
/**
* Interface that is exposed to remote callers to manipulate desktop mode features.
*/
@@ -24,6 +26,18 @@ interface IDesktopMode {
/** Show apps on the desktop on the given display */
void showDesktopApps(int displayId);
+ /** Stash apps on the desktop to allow launching another app from home screen */
+ void stashDesktopApps(int displayId);
+
+ /** Hide apps that may be stashed */
+ void hideStashedDesktopApps(int displayId);
+
+ /** Bring task with the given id to front */
+ oneway void showDesktopApp(int taskId);
+
/** Get count of visible desktop tasks on the given display */
int getVisibleTaskCount(int displayId);
+
+ /** Set listener that will receive callbacks about updates to desktop tasks */
+ oneway void setTaskListener(IDesktopTaskListener listener);
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
new file mode 100644
index 000000000000..39128a863ec9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode;
+
+/**
+ * Allows external processes to register a listener in WMShell to get updates about desktop task
+ * state.
+ */
+interface IDesktopTaskListener {
+
+ /** Desktop task visibility has change. Visible if at least 1 task is visible. */
+ oneway void onVisibilityChanged(int displayId, boolean visible);
+
+ /** Desktop task stashed status has changed. */
+ oneway void onStashedChanged(int displayId, boolean stashed);
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
new file mode 100644
index 000000000000..94788e45e2b6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.animation.Animator
+import android.animation.RectEvaluator
+import android.animation.ValueAnimator
+import android.graphics.Rect
+import android.os.IBinder
+import android.util.SparseArray
+import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerTransaction
+import androidx.core.animation.addListener
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import java.util.function.Supplier
+
+/** Handles the animation of quick resizing of desktop tasks. */
+class ToggleResizeDesktopTaskTransitionHandler(
+ private val transitions: Transitions,
+ private val transactionSupplier: Supplier<SurfaceControl.Transaction>
+) : Transitions.TransitionHandler {
+
+ private val rectEvaluator = RectEvaluator(Rect())
+ private val taskToDecorationMap = SparseArray<DesktopModeWindowDecoration>()
+
+ private var boundsAnimator: Animator? = null
+
+ constructor(
+ transitions: Transitions
+ ) : this(transitions, Supplier { SurfaceControl.Transaction() })
+
+ /** Starts a quick resize transition. */
+ fun startTransition(
+ wct: WindowContainerTransaction,
+ taskId: Int,
+ windowDecoration: DesktopModeWindowDecoration
+ ) {
+ // Pause relayout until the transition animation finishes.
+ windowDecoration.incrementRelayoutBlock()
+ transitions.startTransition(TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE, wct, this)
+ taskToDecorationMap.put(taskId, windowDecoration)
+ }
+
+ override fun startAnimation(
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction,
+ finishCallback: Transitions.TransitionFinishCallback
+ ): Boolean {
+ val change = findRelevantChange(info)
+ val leash = change.leash
+ val taskId = change.taskInfo.taskId
+ val startBounds = change.startAbsBounds
+ val endBounds = change.endAbsBounds
+ val windowDecor =
+ taskToDecorationMap.removeReturnOld(taskId)
+ ?: throw IllegalStateException("Window decoration not found for task $taskId")
+
+ val tx = transactionSupplier.get()
+ boundsAnimator?.cancel()
+ boundsAnimator =
+ ValueAnimator.ofObject(rectEvaluator, startBounds, endBounds)
+ .setDuration(RESIZE_DURATION_MS)
+ .apply {
+ addListener(
+ onStart = {
+ startTransaction
+ .setPosition(
+ leash,
+ startBounds.left.toFloat(),
+ startBounds.top.toFloat()
+ )
+ .setWindowCrop(leash, startBounds.width(), startBounds.height())
+ .show(leash)
+ windowDecor.showResizeVeil(startTransaction, startBounds)
+ },
+ onEnd = {
+ finishTransaction
+ .setPosition(
+ leash,
+ endBounds.left.toFloat(),
+ endBounds.top.toFloat()
+ )
+ .setWindowCrop(leash, endBounds.width(), endBounds.height())
+ .show(leash)
+ windowDecor.hideResizeVeil()
+ finishCallback.onTransitionFinished(null, null)
+ boundsAnimator = null
+ }
+ )
+ addUpdateListener { anim ->
+ val rect = anim.animatedValue as Rect
+ tx.setPosition(leash, rect.left.toFloat(), rect.top.toFloat())
+ .setWindowCrop(leash, rect.width(), rect.height())
+ .show(leash)
+ windowDecor.updateResizeVeil(tx, rect)
+ }
+ start()
+ }
+ return true
+ }
+
+ override fun handleRequest(
+ transition: IBinder,
+ request: TransitionRequestInfo
+ ): WindowContainerTransaction? {
+ return null
+ }
+
+ private fun findRelevantChange(info: TransitionInfo): TransitionInfo.Change {
+ val matchingChanges =
+ info.changes.filter { c ->
+ !isWallpaper(c) && isValidTaskChange(c) && c.mode == TRANSIT_CHANGE
+ }
+ if (matchingChanges.size != 1) {
+ throw IllegalStateException(
+ "Expected 1 relevant change but found: ${matchingChanges.size}"
+ )
+ }
+ return matchingChanges.first()
+ }
+
+ private fun isWallpaper(change: TransitionInfo.Change): Boolean {
+ return (change.flags and TransitionInfo.FLAG_IS_WALLPAPER) != 0
+ }
+
+ private fun isValidTaskChange(change: TransitionInfo.Change): Boolean {
+ return change.taskInfo != null && change.taskInfo?.taskId != -1
+ }
+
+ companion object {
+ private const val RESIZE_DURATION_MS = 300L
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index 99922fbc2d95..f9ea1d4e2a07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -21,11 +21,17 @@ This code itself will not compile by itself, but the `protologtool` will preproc
building to check the log state (is enabled) before printing the print format style log.
**Notes**
-- ProtoLogs currently only work from soong builds (ie. via make/mp). We need to reimplement the
- tool for use with SysUI-studio
+- ProtoLogs are only fully supported from soong builds (ie. via make/mp). In SysUI-studio it falls
+ back to log via Logcat
- Non-text ProtoLogs are not currently supported with the Shell library (you can't view them with
traces in Winscope)
+### Kotlin
+
+Protolog tool does not yet have support for Kotlin code (see [b/168581922](https://b.corp.google.com/issues/168581922)).
+For logging in Kotlin, use the [KtProtoLog](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt)
+class which has a similar API to the Java ProtoLog class.
+
### Enabling ProtoLog command line logging
Run these commands to enable protologs for both WM Core and WM Shell to print to logcat.
```shell
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 04fc79acadbd..55e34fe3d836 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -19,9 +19,15 @@ package com.android.wm.shell.freeform;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
+import android.content.Context;
+import android.graphics.Rect;
import android.os.IBinder;
+import android.util.ArrayMap;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -31,6 +37,8 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -39,23 +47,37 @@ import java.util.ArrayList;
import java.util.List;
/**
- * The {@link Transitions.TransitionHandler} that handles freeform task maximizing and restoring
- * transitions.
+ * The {@link Transitions.TransitionHandler} that handles freeform task maximizing, closing, and
+ * restoring transitions.
*/
public class FreeformTaskTransitionHandler
implements Transitions.TransitionHandler, FreeformTaskTransitionStarter {
-
+ private static final int CLOSE_ANIM_DURATION = 400;
+ private final Context mContext;
private final Transitions mTransitions;
private final WindowDecorViewModel mWindowDecorViewModel;
+ private final DisplayController mDisplayController;
+ private final ShellExecutor mMainExecutor;
+ private final ShellExecutor mAnimExecutor;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
+ private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
+
public FreeformTaskTransitionHandler(
ShellInit shellInit,
Transitions transitions,
- WindowDecorViewModel windowDecorViewModel) {
+ Context context,
+ WindowDecorViewModel windowDecorViewModel,
+ DisplayController displayController,
+ ShellExecutor mainExecutor,
+ ShellExecutor animExecutor) {
mTransitions = transitions;
+ mContext = context;
mWindowDecorViewModel = windowDecorViewModel;
+ mDisplayController = displayController;
+ mMainExecutor = mainExecutor;
+ mAnimExecutor = animExecutor;
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
shellInit.addInitCallback(this::onInit, this);
}
@@ -103,6 +125,14 @@ public class FreeformTaskTransitionHandler
@NonNull SurfaceControl.Transaction finishT,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
boolean transitionHandled = false;
+ final ArrayList<Animator> animations = new ArrayList<>();
+ final Runnable onAnimFinish = () -> {
+ if (!animations.isEmpty()) return;
+ mMainExecutor.execute(() -> {
+ mAnimations.remove(transition);
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ });
+ };
for (TransitionInfo.Change change : info.getChanges()) {
if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
continue;
@@ -121,21 +151,45 @@ public class FreeformTaskTransitionHandler
case WindowManager.TRANSIT_TO_BACK:
transitionHandled |= startMinimizeTransition(transition);
break;
+ case WindowManager.TRANSIT_CLOSE:
+ if (change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ transitionHandled |= startCloseTransition(transition, change,
+ finishT, animations, onAnimFinish);
+ }
+ break;
}
}
-
- mPendingTransitionTokens.remove(transition);
-
if (!transitionHandled) {
return false;
}
-
+ mAnimations.put(transition, animations);
+ // startT must be applied before animations start.
startT.apply();
- mTransitions.getMainExecutor().execute(
- () -> finishCallback.onTransitionFinished(null, null));
+ mAnimExecutor.execute(() -> {
+ for (Animator anim : animations) {
+ anim.start();
+ }
+ });
+ // Run this here in case no animators are created.
+ onAnimFinish.run();
+ mPendingTransitionTokens.remove(transition);
return true;
}
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ArrayList<Animator> animations = mAnimations.get(mergeTarget);
+ if (animations == null) return;
+ mAnimExecutor.execute(() -> {
+ for (Animator anim : animations) {
+ anim.end();
+ }
+ });
+
+ }
+
private boolean startChangeTransition(
IBinder transition,
int type,
@@ -165,6 +219,36 @@ public class FreeformTaskTransitionHandler
return mPendingTransitionTokens.contains(transition);
}
+ private boolean startCloseTransition(IBinder transition, TransitionInfo.Change change,
+ SurfaceControl.Transaction finishT, ArrayList<Animator> animations,
+ Runnable onAnimFinish) {
+ if (!mPendingTransitionTokens.contains(transition)) return false;
+ int screenHeight = mDisplayController
+ .getDisplayLayout(change.getTaskInfo().displayId).height();
+ ValueAnimator animator = new ValueAnimator();
+ animator.setDuration(CLOSE_ANIM_DURATION)
+ .setFloatValues(0f, 1f);
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ SurfaceControl sc = change.getLeash();
+ finishT.hide(sc);
+ Rect startBounds = new Rect(change.getTaskInfo().configuration.windowConfiguration
+ .getBounds());
+ animator.addUpdateListener(animation -> {
+ t.setPosition(sc, startBounds.left,
+ startBounds.top + (animation.getAnimatedFraction() * screenHeight));
+ t.apply();
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ animations.remove(animator);
+ onAnimFinish.run();
+ }
+ });
+ animations.add(animator);
+ return true;
+ }
+
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 9729a4007bac..da455f85d908 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -33,9 +33,10 @@ import android.view.WindowManager;
import androidx.annotation.NonNull;
import com.android.wm.shell.R;
-import com.android.wm.shell.bubbles.DismissView;
-import com.android.wm.shell.common.DismissCircleView;
+import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.bubbles.DismissCircleView;
+import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import com.android.wm.shell.pip.PipUiEventLogger;
@@ -106,6 +107,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
}
mTargetViewContainer = new DismissView(mContext);
+ DismissViewUtils.setup(mTargetViewContainer);
mTargetView = mTargetViewContainer.getCircle();
mTargetViewContainer.setOnApplyWindowInsetsListener((view, windowInsets) -> {
if (!windowInsets.equals(mWindowInsets)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 2a61445b27ba..b0fa9936d879 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -49,7 +49,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM_SPLIT_SCREEN),
WM_SHELL_SYSUI_EVENTS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
- WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_FLOATING_APPS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 843e5af67af9..837f11803ab2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -567,7 +567,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
&& 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 cancel the merge here.
- cancel("task #" + taskInfo.taskId + " is always_on_top");
+ cancel(false /* toHome */, false /* withScreenshots */,
+ "task #" + taskInfo.taskId + " is always_on_top");
return;
}
final boolean isRootTask = taskInfo != null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index 89538cb394d4..e52235fda80f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -24,6 +24,9 @@ import android.window.WindowContainerTransaction;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
+
+import java.util.Optional;
/**
* Main stage for split-screen mode. When split-screen is active all standard activity types launch
@@ -35,9 +38,10 @@ class MainStage extends StageTaskListener {
MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider) {
+ SurfaceSession surfaceSession, IconProvider iconProvider,
+ Optional<WindowDecorViewModel> windowDecorViewModel) {
super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
- iconProvider);
+ iconProvider, windowDecorViewModel);
}
boolean isActive() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 8639b36faf4c..9903113c5453 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -25,6 +25,9 @@ import android.window.WindowContainerTransaction;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
+
+import java.util.Optional;
/**
* Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up
@@ -37,9 +40,10 @@ class SideStage extends StageTaskListener {
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider) {
+ SurfaceSession surfaceSession, IconProvider iconProvider,
+ Optional<WindowDecorViewModel> windowDecorViewModel) {
super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
- iconProvider);
+ iconProvider, windowDecorViewModel);
}
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index e7a367f1992d..e294229038f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -76,6 +76,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
@@ -94,6 +95,7 @@ import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -171,6 +173,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final TransactionPool mTransactionPool;
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
+ private final LaunchAdjacentController mLaunchAdjacentController;
+ private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
private final String[] mAppsSupportMultiInstances;
@@ -197,6 +201,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
TransactionPool transactionPool,
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
+ LaunchAdjacentController launchAdjacentController,
+ Optional<WindowDecorViewModel> windowDecorViewModel,
ShellExecutor mainExecutor) {
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
@@ -213,6 +219,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mTransactionPool = transactionPool;
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
+ mLaunchAdjacentController = launchAdjacentController;
+ mWindowDecorViewModel = windowDecorViewModel;
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
@@ -242,6 +250,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
TransactionPool transactionPool,
IconProvider iconProvider,
RecentTasksController recentTasks,
+ LaunchAdjacentController launchAdjacentController,
+ WindowDecorViewModel windowDecorViewModel,
ShellExecutor mainExecutor,
StageCoordinator stageCoordinator) {
mShellCommandHandler = shellCommandHandler;
@@ -259,6 +269,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mTransactionPool = transactionPool;
mIconProvider = iconProvider;
mRecentTasksOptional = Optional.of(recentTasks);
+ mLaunchAdjacentController = launchAdjacentController;
+ mWindowDecorViewModel = Optional.of(windowDecorViewModel);
mStageCoordinator = stageCoordinator;
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
shellInit.addInitCallback(this::onInit, this);
@@ -291,13 +303,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator = createStageCoordinator();
}
mDragAndDropController.ifPresent(controller -> controller.setSplitScreenController(this));
+ mWindowDecorViewModel.ifPresent(viewModel -> viewModel.setSplitScreenController(this));
}
protected StageCoordinator createStageCoordinator() {
return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mTransitions, mTransactionPool,
- mIconProvider, mMainExecutor, mRecentTasksOptional);
+ mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
+ mMainExecutor, mRecentTasksOptional, mLaunchAdjacentController,
+ mWindowDecorViewModel);
}
@Override
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 64f759856899..bf70d48e5801 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
@@ -121,6 +121,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -138,6 +139,7 @@ import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.SplitBounds;
import com.android.wm.shell.util.TransitionUtil;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -193,6 +195,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// if user is opening another task(s).
private final ArrayList<Integer> mPausingTasks = new ArrayList<>();
private final Optional<RecentTasksController> mRecentTasks;
+ private final LaunchAdjacentController mLaunchAdjacentController;
+ private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
private final Rect mTempRect1 = new Rect();
private final Rect mTempRect2 = new Rect();
@@ -270,7 +274,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool,
IconProvider iconProvider, ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks) {
+ Optional<RecentTasksController> recentTasks,
+ LaunchAdjacentController launchAdjacentController,
+ Optional<WindowDecorViewModel> windowDecorViewModel) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -278,6 +284,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
+ mLaunchAdjacentController = launchAdjacentController;
+ mWindowDecorViewModel = windowDecorViewModel;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
@@ -288,7 +296,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainStageListener,
mSyncQueue,
mSurfaceSession,
- iconProvider);
+ iconProvider,
+ mWindowDecorViewModel);
mSideStage = new SideStage(
mContext,
mTaskOrganizer,
@@ -296,7 +305,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStageListener,
mSyncQueue,
mSurfaceSession,
- iconProvider);
+ iconProvider,
+ mWindowDecorViewModel);
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
@@ -323,7 +333,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks) {
+ Optional<RecentTasksController> recentTasks,
+ LaunchAdjacentController launchAdjacentController,
+ Optional<WindowDecorViewModel> windowDecorViewModel) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -340,6 +352,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
+ mLaunchAdjacentController = launchAdjacentController;
+ mWindowDecorViewModel = windowDecorViewModel;
mDisplayController.addDisplayWindowListener(this);
transitions.addHandler(this);
mSplitUnsupportedToast = Toast.makeText(mContext,
@@ -1733,7 +1747,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mRootTaskInfo == null || mRootTaskInfo.taskId != taskInfo.taskId) {
throw new IllegalArgumentException(this + "\n Unknown task info changed: " + taskInfo);
}
-
+ mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo));
mRootTaskInfo = taskInfo;
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
@@ -1781,7 +1795,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true);
// Make the stages adjacent to each other so they occlude what's behind them.
wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
- wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
setRootForceTranslucent(true, wct);
mSplitLayout.getInvisibleBounds(mTempRect1);
wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
@@ -1789,6 +1802,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSyncQueue.runInSync(t -> {
t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.top);
});
+ mLaunchAdjacentController.setLaunchAdjacentRoot(mSideStage.mRootTaskInfo.token);
}
/** Callback when split roots have child task appeared under it, this is a little different from
@@ -1818,9 +1832,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void onRootTaskVanished() {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- if (mRootTaskInfo != null) {
- wct.clearLaunchAdjacentFlagRoot(mRootTaskInfo.token);
- }
+ mLaunchAdjacentController.clearLaunchAdjacentRoot();
applyExitSplitScreen(null /* childrenToTop */, wct, EXIT_REASON_ROOT_TASK_VANISHED);
mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, mSplitLayout);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index a01eddbc9b9f..3ef4f024a8ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -51,8 +51,10 @@ import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.io.PrintWriter;
+import java.util.Optional;
import java.util.function.Predicate;
/**
@@ -87,6 +89,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
private final SurfaceSession mSurfaceSession;
private final SyncTransactionQueue mSyncQueue;
private final IconProvider mIconProvider;
+ private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
protected SurfaceControl mRootLeash;
@@ -98,12 +101,14 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider) {
+ SurfaceSession surfaceSession, IconProvider iconProvider,
+ Optional<WindowDecorViewModel> windowDecorViewModel) {
mContext = context;
mCallbacks = callbacks;
mSyncQueue = syncQueue;
mSurfaceSession = surfaceSession;
mIconProvider = iconProvider;
+ mWindowDecorViewModel = windowDecorViewModel;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
}
@@ -202,6 +207,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
@Override
@CallSuper
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo));
if (mRootTaskInfo.taskId == taskInfo.taskId) {
// Inflates split decor view only when the root task is visible.
if (!ENABLE_SHELL_TRANSITIONS && mRootTaskInfo.isVisible != taskInfo.isVisible) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index 27d520d81c41..a2301b133426 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -27,6 +27,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -58,6 +59,7 @@ public class TvSplitScreenController extends SplitScreenController {
private final TransactionPool mTransactionPool;
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
+ private final LaunchAdjacentController mLaunchAdjacentController;
private final Handler mMainHandler;
private final SystemWindows mSystemWindows;
@@ -77,13 +79,15 @@ public class TvSplitScreenController extends SplitScreenController {
TransactionPool transactionPool,
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
+ LaunchAdjacentController launchAdjacentController,
ShellExecutor mainExecutor,
Handler mainHandler,
SystemWindows systemWindows) {
super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer,
syncQueue, rootTDAOrganizer, displayController, displayImeController,
displayInsetsController, dragAndDropController, transitions, transactionPool,
- iconProvider, recentTasks, mainExecutor);
+ iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
+ mainExecutor);
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
@@ -96,6 +100,7 @@ public class TvSplitScreenController extends SplitScreenController {
mTransactionPool = transactionPool;
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
+ mLaunchAdjacentController = launchAdjacentController;
mMainHandler = mainHandler;
mSystemWindows = systemWindows;
@@ -111,7 +116,7 @@ public class TvSplitScreenController extends SplitScreenController {
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool,
mIconProvider, mMainExecutor, mMainHandler,
- mRecentTasksOptional, mSystemWindows);
+ mRecentTasksOptional, mLaunchAdjacentController, mSystemWindows);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
index 4d563fbb7f04..79476919221e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -24,6 +24,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -51,10 +52,11 @@ public class TvStageCoordinator extends StageCoordinator
IconProvider iconProvider, ShellExecutor mainExecutor,
Handler mainHandler,
Optional<RecentTasksController> recentTasks,
+ LaunchAdjacentController launchAdjacentController,
SystemWindows systemWindows) {
super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
- mainExecutor, recentTasks);
+ mainExecutor, recentTasks, launchAdjacentController, Optional.empty());
mTvSplitMenuController = new TvSplitMenuController(context, this,
systemWindows, mainHandler);
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 d0a361a8ecd2..d9edde16a863 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
@@ -44,6 +44,9 @@ import android.window.WindowContainerTransactionCallback;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.split.SplitScreenUtils;
+import com.android.wm.shell.desktopmode.DesktopModeController;
+import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
@@ -70,6 +73,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
private RecentsTransitionHandler mRecentsHandler;
private StageCoordinator mSplitHandler;
private final KeyguardTransitionHandler mKeyguardHandler;
+ private DesktopModeController mDesktopModeController;
+ private DesktopTasksController mDesktopTasksController;
private UnfoldTransitionHandler mUnfoldHandler;
private static class MixedTransition {
@@ -87,8 +92,11 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
/** Keyguard exit/occlude/unocclude transition. */
static final int TYPE_KEYGUARD = 5;
+ /** Recents Transition while in desktop mode. */
+ static final int TYPE_RECENTS_DURING_DESKTOP = 6;
+
/** Fuld/Unfold transition. */
- static final int TYPE_UNFOLD = 6;
+ static final int TYPE_UNFOLD = 7;
/** The default animation for this mixed transition. */
static final int ANIM_TYPE_DEFAULT = 0;
@@ -142,6 +150,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
Optional<PipTouchHandler> pipTouchHandlerOptional,
Optional<RecentsTransitionHandler> recentsHandlerOptional,
KeyguardTransitionHandler keyguardHandler,
+ Optional<DesktopModeController> desktopModeControllerOptional,
+ Optional<DesktopTasksController> desktopTasksControllerOptional,
Optional<UnfoldTransitionHandler> unfoldHandler) {
mPlayer = player;
mKeyguardHandler = keyguardHandler;
@@ -159,6 +169,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
if (mRecentsHandler != null) {
mRecentsHandler.addMixer(this);
}
+ mDesktopModeController = desktopModeControllerOptional.orElse(null);
+ mDesktopTasksController = desktopTasksControllerOptional.orElse(null);
mUnfoldHandler = unfoldHandler.orElse(null);
}, this);
}
@@ -239,7 +251,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
@Override
public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) {
- if (mRecentsHandler != null && mSplitHandler.isSplitScreenVisible()) {
+ if (mRecentsHandler != null && (mSplitHandler.isSplitScreenVisible()
+ || DesktopModeStatus.isActive(mPlayer.getContext()))) {
return this;
}
return null;
@@ -254,6 +267,13 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
mixed.mLeftoversHandler = mRecentsHandler;
mActiveTransitions.add(mixed);
+ } else if (DesktopModeStatus.isActive(mPlayer.getContext())) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ + "desktop mode is active, so treat it as Mixed.");
+ final MixedTransition mixed = new MixedTransition(
+ MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition);
+ mixed.mLeftoversHandler = mRecentsHandler;
+ mActiveTransitions.add(mixed);
} else {
throw new IllegalStateException("Accepted a recents transition but don't know how to"
+ " handle it");
@@ -340,6 +360,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
} else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
return animateKeyguard(mixed, info, startTransaction, finishTransaction,
finishCallback);
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
+ return animateRecentsDuringDesktop(mixed, info, startTransaction, finishTransaction,
+ finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_UNFOLD) {
return animateUnfold(mixed, info, startTransaction, finishTransaction, finishCallback);
} else {
@@ -641,6 +664,30 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
return true;
}
+ private boolean animateRecentsDuringDesktop(@NonNull final MixedTransition mixed,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ boolean consumed = mRecentsHandler.startAnimation(
+ mixed.mTransition, info, startTransaction, finishTransaction, finishCallback);
+ if (!consumed) {
+ return false;
+ }
+ //Sync desktop mode state (proto 1)
+ if (mDesktopModeController != null) {
+ mDesktopModeController.syncSurfaceState(info, finishTransaction);
+ return true;
+ }
+ //Sync desktop mode state (proto 2)
+ if (mDesktopTasksController != null) {
+ mDesktopTasksController.syncSurfaceState(info, finishTransaction);
+ return true;
+ }
+
+ return false;
+ }
+
private boolean animateUnfold(@NonNull final MixedTransition mixed,
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -727,6 +774,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
+ mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
+ finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_UNFOLD) {
mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
} else {
@@ -754,6 +804,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
} else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT);
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
+ mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
} else if (mixed.mType == MixedTransition.TYPE_UNFOLD) {
mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT);
}
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 2327d86ab618..75659960bc32 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
@@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
@@ -161,6 +162,10 @@ public class Transitions implements RemoteCallable<Transitions>,
public static final int TRANSIT_CANCEL_ENTERING_DESKTOP_MODE =
WindowManager.TRANSIT_FIRST_CUSTOM + 13;
+ /** Transition type to animate the toggle resize between the max and default desktop sizes. */
+ public static final int TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE =
+ WindowManager.TRANSIT_FIRST_CUSTOM + 14;
+
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
@@ -724,11 +729,15 @@ public class Transitions implements RemoteCallable<Transitions>,
final int changeSize = info.getChanges().size();
boolean taskChange = false;
boolean transferStartingWindow = false;
+ int noAnimationBehindStartingWindow = 0;
boolean allOccluded = changeSize > 0;
for (int i = changeSize - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
taskChange |= change.getTaskInfo() != null;
transferStartingWindow |= change.hasFlags(FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT);
+ if (change.hasAllFlags(FLAG_IS_BEHIND_STARTING_WINDOW | FLAG_NO_ANIMATION)) {
+ noAnimationBehindStartingWindow++;
+ }
if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
allOccluded = false;
}
@@ -736,9 +745,11 @@ public class Transitions implements RemoteCallable<Transitions>,
// There does not need animation when:
// A. Transfer starting window. Apply transfer starting window directly if there is no other
// task change. Since this is an activity->activity situation, we can detect it by selecting
- // transitions with only 2 changes where neither are tasks and one is a starting-window
- // recipient.
- if (!taskChange && transferStartingWindow && changeSize == 2
+ // transitions with only 2 changes where
+ // 1. neither are tasks, and
+ // 2. one is a starting-window recipient, or all change is behind starting window.
+ if (!taskChange && (transferStartingWindow || noAnimationBehindStartingWindow == changeSize)
+ && changeSize == 2
// B. It's visibility change if the TRANSIT_TO_BACK/TO_FRONT happened when all
// changes are underneath another change.
|| ((info.getType() == TRANSIT_TO_BACK || info.getType() == TRANSIT_TO_FRONT)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
index f81fc6fbea49..6bba0d1386fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
@@ -110,7 +110,7 @@ public class FullscreenUnfoldTaskAnimator implements UnfoldTaskAnimator,
for (int i = state.sourceSize() - 1; i >= 0; i--) {
final InsetsSource source = state.sourceAt(i);
if (source.getType() == WindowInsets.Type.navigationBars()
- && source.insetsRoundedCornerFrame()) {
+ && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) {
return source;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
index a4cf149cc3b5..21994a997be5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
@@ -50,11 +50,11 @@ import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldBackgroundController;
+import dagger.Lazy;
+
import java.util.Optional;
import java.util.concurrent.Executor;
-import dagger.Lazy;
-
/**
* This helper class contains logic that calculates scaling and cropping parameters
* for the folding/unfolding animation. As an input it receives TaskInfo objects and
@@ -149,7 +149,7 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
for (int i = state.sourceSize() - 1; i >= 0; i--) {
final InsetsSource source = state.sourceAt(i);
if (source.getType() == WindowInsets.Type.navigationBars()
- && source.insetsRoundedCornerFrame()) {
+ && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) {
return source;
}
}
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 39fb7936747e..cf1692018518 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
@@ -33,11 +33,14 @@ import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import androidx.annotation.Nullable;
+
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
/**
@@ -89,6 +92,9 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
@Override
+ public void setSplitScreenController(SplitScreenController splitScreenController) {}
+
+ @Override
public boolean onTaskOpening(
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
@@ -187,7 +193,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
final DragPositioningCallback dragPositioningCallback =
new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController,
- null /* disallowedAreaForEndBounds */);
+ 0 /* disallowedAreaForEndBoundsHeight */);
final CaptionTouchEventListener touchEventListener =
new CaptionTouchEventListener(taskInfo, dragPositioningCallback);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
@@ -254,7 +260,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
* @return {@code true} if a drag is happening; or {@code false} if it is not
*/
@Override
- public boolean handleMotionEvent(MotionEvent e) {
+ public boolean handleMotionEvent(@Nullable View v, MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
return false;
@@ -268,7 +274,10 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
return false;
}
case MotionEvent.ACTION_MOVE: {
- int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+ if (e.findPointerIndex(mDragPointerId) == -1) {
+ mDragPointerId = e.getPointerId(0);
+ }
+ final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mIsDragging = true;
@@ -276,7 +285,10 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
- int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+ if (e.findPointerIndex(mDragPointerId) == -1) {
+ mDragPointerId = e.getPointerId(0);
+ }
+ final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
final boolean wasDragging = mIsDragging;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 116af7094e13..b217bd39a446 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -113,7 +113,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mLayoutResId = R.layout.caption_window_decor;
- mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
+ mRelayoutParams.mCaptionHeightId = getCaptionHeightId();
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
@@ -143,6 +143,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
mHandler,
mChoreographer,
mDisplay.getDisplayId(),
+ 0 /* taskCornerRadius */,
mDecorationContainerSurface,
mDragPositioningCallback);
}
@@ -221,4 +222,9 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
closeDragResizeListener();
super.close();
}
+
+ @Override
+ int getCaptionHeightId() {
+ return R.dimen.freeform_decor_caption_height;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 9fd57d7e1201..7245bc91cfca 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
@@ -19,6 +19,8 @@ package com.android.wm.shell.windowdecor;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -42,6 +44,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.util.SparseArray;
import android.view.Choreographer;
+import android.view.GestureDetector;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
@@ -53,6 +56,7 @@ import android.view.View;
import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -106,7 +110,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
private final Transitions mTransitions;
- private Optional<SplitScreenController> mSplitScreenController;
+ private SplitScreenController mSplitScreenController;
private ValueAnimator mDragToDesktopValueAnimator;
private final Rect mDragToDesktopAnimationStartBounds = new Rect();
@@ -121,8 +125,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopModeController> desktopModeController,
- Optional<DesktopTasksController> desktopTasksController,
- Optional<SplitScreenController> splitScreenController) {
+ Optional<DesktopTasksController> desktopTasksController) {
this(
context,
mainHandler,
@@ -133,7 +136,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
transitions,
desktopModeController,
desktopTasksController,
- splitScreenController,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
SurfaceControl.Transaction::new);
@@ -150,7 +152,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
Transitions transitions,
Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController,
- Optional<SplitScreenController> splitScreenController,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory) {
@@ -160,7 +161,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
mTaskOrganizer = taskOrganizer;
mDisplayController = displayController;
- mSplitScreenController = splitScreenController;
mSyncQueue = syncQueue;
mTransitions = transitions;
mDesktopModeController = desktopModeController;
@@ -177,6 +177,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
@Override
+ public void setSplitScreenController(SplitScreenController splitScreenController) {
+ mSplitScreenController = splitScreenController;
+ }
+
+ @Override
public boolean onTaskOpening(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
@@ -193,7 +198,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change) {
if (change.getMode() == WindowManager.TRANSIT_CHANGE
- && (info.getType() == Transitions.TRANSIT_ENTER_DESKTOP_MODE)) {
+ && (info.getType() == Transitions.TRANSIT_ENTER_DESKTOP_MODE
+ || info.getType() == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
+ || info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
+ || info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE)) {
mWindowDecorByTaskId.get(change.getTaskInfo().taskId)
.addTransitionPausingRelayout(transition);
}
@@ -236,7 +244,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
-
if (!shouldShowWindowDecor(taskInfo)) {
if (decoration != null) {
destroyWindowDecoration(taskInfo);
@@ -275,15 +282,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
}
- private class DesktopModeTouchEventListener implements
- View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
+ private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
+ implements View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
private final int mTaskId;
private final WindowContainerToken mTaskToken;
private final DragPositioningCallback mDragPositioningCallback;
private final DragDetector mDragDetector;
+ private final GestureDetector mGestureDetector;
private boolean mIsDragging;
+ private boolean mShouldClick;
private int mDragPointerId = -1;
private DesktopModeTouchEventListener(
@@ -293,6 +302,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mTaskToken = taskInfo.token;
mDragPositioningCallback = dragPositioningCallback;
mDragDetector = new DragDetector(this);
+ mGestureDetector = new GestureDetector(mContext, this);
}
@Override
@@ -301,14 +311,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final int id = v.getId();
if (id == R.id.close_window || id == R.id.close_button) {
mTaskOperations.closeTask(mTaskToken);
- if (mSplitScreenController.isPresent()
- && mSplitScreenController.get().isSplitScreenVisible()) {
- int remainingTaskPosition = mTaskId == mSplitScreenController.get()
+ if (mSplitScreenController != null
+ && mSplitScreenController.isSplitScreenVisible()) {
+ int remainingTaskPosition = mTaskId == mSplitScreenController
.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
- ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController.get()
+ ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController
.getTaskInfo(remainingTaskPosition);
- mSplitScreenController.get().moveTaskToFullscreen(remainingTask.taskId);
+ mSplitScreenController.moveTaskToFullscreen(remainingTask.taskId);
}
} else if (id == R.id.back_button) {
mTaskOperations.injectBackKey();
@@ -321,7 +331,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
} else if (id == R.id.desktop_button) {
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
- mDesktopTasksController.ifPresent(c -> c.moveToDesktop(mTaskId));
+ if (mDesktopTasksController.isPresent()) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ // App sometimes draws before the insets from WindowDecoration#relayout have
+ // been added, so they must be added here
+ mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct);
+ mDesktopTasksController.get().moveToDesktop(mTaskId, wct);
+ }
decoration.closeHandleMenu();
} else if (id == R.id.fullscreen_button) {
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
@@ -347,7 +363,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return false;
}
moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
- return mDragDetector.onMotionEvent(e);
+ return mDragDetector.onMotionEvent(v, e);
}
private void moveTaskToFront(RunningTaskInfo taskInfo) {
@@ -362,7 +378,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
* @return {@code true} if the motion event is handled.
*/
@Override
- public boolean handleMotionEvent(MotionEvent e) {
+ public boolean handleMotionEvent(@Nullable View v, MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (DesktopModeStatus.isProto2Enabled()
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
@@ -373,6 +389,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
== WINDOWING_MODE_FULLSCREEN) {
return false;
}
+ if (mGestureDetector.onTouchEvent(e)) {
+ return true;
+ }
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mDragPointerId = e.getPointerId(0);
@@ -380,21 +399,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
0 /* ctrlType */, e.getRawX(0),
e.getRawY(0));
mIsDragging = false;
- return false;
+ mShouldClick = true;
+ return true;
}
case MotionEvent.ACTION_MOVE: {
final DesktopModeWindowDecoration decoration =
mWindowDecorByTaskId.get(mTaskId);
+ if (e.findPointerIndex(mDragPointerId) == -1) {
+ mDragPointerId = e.getPointerId(0);
+ }
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
mDesktopTasksController.ifPresent(c -> c.onDragPositioningMove(taskInfo,
decoration.mTaskSurface, e.getRawY(dragPointerIdx)));
mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mIsDragging = true;
+ mShouldClick = false;
return true;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
+ final boolean wasDragging = mIsDragging;
+ if (!wasDragging) {
+ if (mShouldClick && v != null) {
+ v.performClick();
+ mShouldClick = false;
+ return true;
+ }
+ return false;
+ }
+ if (e.findPointerIndex(mDragPointerId) == -1) {
+ mDragPointerId = e.getPointerId(0);
+ }
final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
// Position of the task is calculated by subtracting the raw location of the
// motion event (the location of the motion relative to the display) by the
@@ -405,14 +441,22 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo,
- position));
- final boolean wasDragging = mIsDragging;
+ position, e.getRawY(), mWindowDecorByTaskId.get(mTaskId)));
mIsDragging = false;
- return wasDragging;
+ return true;
}
}
return true;
}
+
+ @Override
+ public boolean onDoubleTap(@NonNull MotionEvent e) {
+ final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ mDesktopTasksController.ifPresent(c -> {
+ c.toggleDesktopTaskSize(taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId));
+ });
+ return true;
+ }
}
// InputEventReceiver to listen for touch input outside of caption bounds
@@ -540,9 +584,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
relevantDecor.mTaskInfo.configuration.windowConfiguration.getBounds());
boolean dragFromStatusBarAllowed = false;
if (DesktopModeStatus.isProto2Enabled()) {
- // In proto2 any full screen task can be dragged to freeform
- dragFromStatusBarAllowed = relevantDecor.mTaskInfo.getWindowingMode()
- == WINDOWING_MODE_FULLSCREEN;
+ // In proto2 any full screen or multi-window task can be dragged to
+ // freeform.
+ final int windowingMode = relevantDecor.mTaskInfo.getWindowingMode();
+ dragFromStatusBarAllowed = windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_MULTI_WINDOW;
}
if (dragFromStatusBarAllowed && relevantDecor.checkTouchEventInHandle(ev)) {
@@ -571,9 +617,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return;
} else if (mDragToDesktopAnimationStarted) {
Point position = new Point((int) ev.getX(), (int) ev.getY());
+ relevantDecor.incrementRelayoutBlock();
mDesktopTasksController.ifPresent(
- c -> c.cancelMoveToFreeform(relevantDecor.mTaskInfo,
- position));
+ c -> c.cancelMoveToFreeform(relevantDecor.mTaskInfo, position));
mDragToDesktopAnimationStarted = false;
return;
}
@@ -699,8 +745,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@Nullable
private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
- if (mSplitScreenController.isPresent()
- && mSplitScreenController.get().isSplitScreenVisible()) {
+ if (mSplitScreenController != null && mSplitScreenController.isSplitScreenVisible()) {
// We can't look at focused task here as only one task will have focus.
return getSplitScreenDecor(ev);
} else {
@@ -711,9 +756,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
@Nullable
private DesktopModeWindowDecoration getSplitScreenDecor(MotionEvent ev) {
ActivityManager.RunningTaskInfo topOrLeftTask =
- mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ mSplitScreenController.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
ActivityManager.RunningTaskInfo bottomOrRightTask =
- mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
if (topOrLeftTask != null && topOrLeftTask.getConfiguration()
.windowConfiguration.getBounds().contains((int) ev.getX(), (int) ev.getY())) {
return mWindowDecorByTaskId.get(topOrLeftTask.taskId);
@@ -765,11 +810,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
- if (mSplitScreenController.isPresent()
- && mSplitScreenController.get().isTaskRootOrStageRoot(taskInfo.taskId)) {
+ if (mSplitScreenController != null
+ && mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) {
return false;
}
return DesktopModeStatus.isProto2Enabled()
+ && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
&& taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
&& mDisplayController.getDisplayContext(taskInfo.displayId)
.getResources().getConfiguration().smallestScreenWidthDp >= 600;
@@ -796,9 +842,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mMainChoreographer,
mSyncQueue);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
+ windowDecoration.createResizeVeil();
final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback(
- windowDecoration, taskInfo);
+ windowDecoration);
final DesktopModeTouchEventListener touchEventListener =
new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback);
@@ -811,20 +858,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
incrementEventReceiverTasks(taskInfo.displayId);
}
private DragPositioningCallback createDragPositioningCallback(
- @NonNull DesktopModeWindowDecoration windowDecoration,
- @NonNull RunningTaskInfo taskInfo) {
- final int screenWidth = mDisplayController.getDisplayLayout(taskInfo.displayId).width();
- final Rect disallowedAreaForEndBounds = new Rect(0, 0, screenWidth,
- getStatusBarHeight(taskInfo.displayId));
+ @NonNull DesktopModeWindowDecoration windowDecoration) {
+ final int transitionAreaHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.desktop_mode_transition_area_height);
if (!DesktopModeStatus.isVeiledResizeEnabled()) {
return new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration,
- mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
- mTransactionFactory);
+ mDisplayController, mDragStartListener, mTransactionFactory,
+ transitionAreaHeight);
} else {
- windowDecoration.createResizeVeil();
return new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration,
- mDisplayController, disallowedAreaForEndBounds, mDragStartListener,
- mTransitions);
+ mDisplayController, mDragStartListener, mTransitions,
+ transitionAreaHeight);
}
}
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 ce11b2604559..bc89385a0d13 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
@@ -23,7 +23,6 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.content.res.TypedArray;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -39,6 +38,7 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.window.WindowContainerTransaction;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -178,15 +178,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mLayoutResId = windowDecorLayoutId;
- mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
+ mRelayoutParams.mCaptionHeightId = getCaptionHeightId();
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
- final TypedArray ta = mContext.obtainStyledAttributes(
- new int[]{android.R.attr.dialogCornerRadius});
- mRelayoutParams.mCornerRadius = ta.getDimensionPixelSize(0, 0);
- ta.recycle();
-
+ mRelayoutParams.mCornerRadius =
+ (int) ScreenDecorationsUtils.getWindowCornerRadius(mContext);
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
@@ -235,6 +232,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mHandler,
mChoreographer,
mDisplay.getDisplayId(),
+ mRelayoutParams.mCornerRadius,
mDecorationContainerSurface,
mDragPositioningCallback);
}
@@ -294,23 +292,37 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
/**
- * Fade in the resize veil
+ * Show the resize veil.
*/
- void showResizeVeil(Rect taskBounds) {
+ public void showResizeVeil(Rect taskBounds) {
mResizeVeil.showVeil(mTaskSurface, taskBounds);
}
/**
+ * Show the resize veil.
+ */
+ public void showResizeVeil(SurfaceControl.Transaction tx, Rect taskBounds) {
+ mResizeVeil.showVeil(tx, mTaskSurface, taskBounds, false /* fadeIn */);
+ }
+
+ /**
* Set new bounds for the resize veil
*/
- void updateResizeVeil(Rect newBounds) {
+ public void updateResizeVeil(Rect newBounds) {
mResizeVeil.updateResizeVeil(newBounds);
}
/**
+ * Set new bounds for the resize veil
+ */
+ public void updateResizeVeil(SurfaceControl.Transaction tx, Rect newBounds) {
+ mResizeVeil.updateResizeVeil(tx, newBounds);
+ }
+
+ /**
* Fade the resize veil out.
*/
- void hideResizeVeil() {
+ public void hideResizeVeil() {
mResizeVeil.hideVeil();
}
@@ -479,6 +491,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
}
+ @Override
+ int getCaptionHeightId() {
+ return R.dimen.freeform_decor_caption_height;
+ }
+
/**
* Add transition to mTransitionsPausingRelayout
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index 65b5a7a17afe..da268988bac7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -24,6 +24,9 @@ import static android.view.MotionEvent.ACTION_UP;
import android.graphics.PointF;
import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.Nullable;
/**
* A detector for touch inputs that differentiates between drag and click inputs. It receives a flow
@@ -54,14 +57,24 @@ class DragDetector {
*
* @return the result returned by {@link #mEventHandler}, or the result when
* {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
- */
+ */
boolean onMotionEvent(MotionEvent ev) {
+ return onMotionEvent(null /* view */, ev);
+ }
+
+ /**
+ * The receiver of the {@link MotionEvent} flow.
+ *
+ * @return the result returned by {@link #mEventHandler}, or the result when
+ * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
+ */
+ boolean onMotionEvent(View v, MotionEvent ev) {
final boolean isTouchScreen =
(ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
if (!isTouchScreen) {
// Only touches generate noisy moves, so mouse/trackpad events don't need to filtered
// to take the slop threshold into consideration.
- return mEventHandler.handleMotionEvent(ev);
+ return mEventHandler.handleMotionEvent(v, ev);
}
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
@@ -69,12 +82,15 @@ class DragDetector {
float rawX = ev.getRawX(0);
float rawY = ev.getRawY(0);
mInputDownPoint.set(rawX, rawY);
- mResultOfDownAction = mEventHandler.handleMotionEvent(ev);
+ mResultOfDownAction = mEventHandler.handleMotionEvent(v, ev);
return mResultOfDownAction;
}
case ACTION_MOVE: {
+ if (ev.findPointerIndex(mDragPointerId) == -1) {
+ mDragPointerId = ev.getPointerId(0);
+ }
+ final int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
if (!mIsDragEvent) {
- int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
// Touches generate noisy moves, so only once the move is past the touch
@@ -84,7 +100,7 @@ class DragDetector {
// The event handler should only be notified about 'move' events if a drag has been
// detected.
if (mIsDragEvent) {
- return mEventHandler.handleMotionEvent(ev);
+ return mEventHandler.handleMotionEvent(v, ev);
} else {
return mResultOfDownAction;
}
@@ -92,10 +108,10 @@ class DragDetector {
case ACTION_UP:
case ACTION_CANCEL: {
resetState();
- return mEventHandler.handleMotionEvent(ev);
+ return mEventHandler.handleMotionEvent(v, ev);
}
default:
- return mEventHandler.handleMotionEvent(ev);
+ return mEventHandler.handleMotionEvent(v, ev);
}
}
@@ -111,6 +127,6 @@ class DragDetector {
}
interface MotionEventHandler {
- boolean handleMotionEvent(MotionEvent ev);
+ boolean handleMotionEvent(@Nullable View v, MotionEvent ev);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index 09e29bcbcf9f..e32bd42acf74 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -83,8 +83,6 @@ public class DragPositioningCallbackUtility {
// Make sure the new resizing destination in any direction falls within the stable bounds.
// If not, set the bounds back to the old location that was valid to avoid conflicts with
// some regions such as the gesture area.
- displayController.getDisplayLayout(windowDecoration.mDisplay.getDisplayId())
- .getStableBounds(stableBounds);
if ((ctrlType & CTRL_TYPE_LEFT) != 0) {
final int candidateLeft = repositionTaskBounds.left + (int) delta.x;
repositionTaskBounds.left = (candidateLeft > stableBounds.left)
@@ -136,7 +134,7 @@ public class DragPositioningCallbackUtility {
repositionTaskBounds.top);
}
- static void updateTaskBounds(Rect repositionTaskBounds, Rect taskBoundsAtDragStart,
+ private static void updateTaskBounds(Rect repositionTaskBounds, Rect taskBoundsAtDragStart,
PointF repositionStartPoint, float x, float y) {
final float deltaX = x - repositionStartPoint.x;
final float deltaY = y - repositionStartPoint.y;
@@ -145,6 +143,23 @@ public class DragPositioningCallbackUtility {
}
/**
+ * Updates repositionTaskBounds to the final bounds of the task after the drag is finished. If
+ * the bounds are outside of the stable bounds, they are shifted to place task at the top of the
+ * stable bounds.
+ */
+ static void onDragEnd(Rect repositionTaskBounds, Rect taskBoundsAtDragStart, Rect stableBounds,
+ PointF repositionStartPoint, float x, float y) {
+ updateTaskBounds(repositionTaskBounds, taskBoundsAtDragStart, repositionStartPoint,
+ x, y);
+
+ // If task is outside of stable bounds (in the status bar area), shift the task down.
+ if (stableBounds.top > repositionTaskBounds.top) {
+ final int yShift = stableBounds.top - repositionTaskBounds.top;
+ repositionTaskBounds.offset(0, yShift);
+ }
+ }
+
+ /**
* Apply a bounds change to a task.
* @param windowDecoration decor of task we are changing bounds for
* @param taskBounds new bounds of this task
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 287d86187288..e5fc66afbdfc 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
@@ -42,6 +42,7 @@ import android.view.InputEventReceiver;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.SurfaceControl;
+import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManagerGlobal;
@@ -55,7 +56,6 @@ import com.android.internal.view.BaseIWindow;
*/
class DragResizeInputListener implements AutoCloseable {
private static final String TAG = "DragResizeInputListener";
-
private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();
private final Handler mHandler;
private final Choreographer mChoreographer;
@@ -73,6 +73,7 @@ class DragResizeInputListener implements AutoCloseable {
private int mTaskHeight;
private int mResizeHandleThickness;
private int mCornerSize;
+ private int mTaskCornerRadius;
private Rect mLeftTopCornerBounds;
private Rect mRightTopCornerBounds;
@@ -87,12 +88,14 @@ class DragResizeInputListener implements AutoCloseable {
Handler handler,
Choreographer choreographer,
int displayId,
+ int taskCornerRadius,
SurfaceControl decorationSurface,
DragPositioningCallback callback) {
mInputManager = context.getSystemService(InputManager.class);
mHandler = handler;
mChoreographer = choreographer;
mDisplayId = displayId;
+ mTaskCornerRadius = taskCornerRadius;
mDecorationSurface = decorationSurface;
// Use a fake window as the backing surface is a container layer and we don't want to create
// a buffer layer for it so we can't use ViewRootImpl.
@@ -126,12 +129,7 @@ class DragResizeInputListener implements AutoCloseable {
}
/**
- * Updates geometry of this drag resize handler. Needs to be called every time there is a size
- * change to notify the input event receiver it's ready to take the next input event. Otherwise
- * it'll keep batching move events and the drag resize process is stalled.
- *
- * This is also used to update the touch regions of this handler every event dispatched here is
- * a potential resize request.
+ * Updates the geometry (the touch region) of this drag resize handler.
*
* @param taskWidth The width of the task.
* @param taskHeight The height of the task.
@@ -303,7 +301,7 @@ class DragResizeInputListener implements AutoCloseable {
}
@Override
- public boolean handleMotionEvent(MotionEvent e) {
+ public boolean handleMotionEvent(View v, MotionEvent e) {
boolean result = false;
// Check if this is a touch event vs mouse event.
// Touch events are tracked in four corners. Other events are tracked in resize edges.
@@ -383,19 +381,64 @@ class DragResizeInputListener implements AutoCloseable {
@DragPositioningCallback.CtrlType
private int calculateResizeHandlesCtrlType(float x, float y) {
int ctrlType = 0;
- if (x < 0) {
+ // mTaskCornerRadius is only used in comparing with corner regions. Comparisons with
+ // sides will use the bounds specified in setGeometry and not go into task bounds.
+ if (x < mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_LEFT;
}
- if (x > mTaskWidth) {
+ if (x > mTaskWidth - mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_RIGHT;
}
- if (y < 0) {
+ if (y < mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_TOP;
}
- if (y > mTaskHeight) {
+ if (y > mTaskHeight - mTaskCornerRadius) {
ctrlType |= CTRL_TYPE_BOTTOM;
}
- return ctrlType;
+ return checkDistanceFromCenter(ctrlType, x, y);
+ }
+
+ // If corner input is not within appropriate distance of corner radius, do not use it.
+ // If input is not on a corner or is within valid distance, return ctrlType.
+ @DragPositioningCallback.CtrlType
+ private int checkDistanceFromCenter(@DragPositioningCallback.CtrlType int ctrlType,
+ float x, float y) {
+ int centerX;
+ int centerY;
+
+ // Determine center of rounded corner circle; this is simply the corner if radius is 0.
+ switch (ctrlType) {
+ case CTRL_TYPE_LEFT | CTRL_TYPE_TOP: {
+ centerX = mTaskCornerRadius;
+ centerY = mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM: {
+ centerX = mTaskCornerRadius;
+ centerY = mTaskHeight - mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_RIGHT | CTRL_TYPE_TOP: {
+ centerX = mTaskWidth - mTaskCornerRadius;
+ centerY = mTaskCornerRadius;
+ break;
+ }
+ case CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM: {
+ centerX = mTaskWidth - mTaskCornerRadius;
+ centerY = mTaskHeight - mTaskCornerRadius;
+ break;
+ }
+ default: {
+ return ctrlType;
+ }
+ }
+ double distanceFromCenter = Math.hypot(x - centerX, y - centerY);
+
+ if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness
+ && distanceFromCenter >= mTaskCornerRadius) {
+ return ctrlType;
+ }
+ return 0;
}
@DragPositioningCallback.CtrlType
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
index 9082323452c9..917abf5524a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositioner.java
@@ -21,8 +21,6 @@ import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
-import androidx.annotation.Nullable;
-
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -42,28 +40,31 @@ class FluidResizeTaskPositioner implements DragPositioningCallback {
private final Rect mTaskBoundsAtDragStart = new Rect();
private final PointF mRepositionStartPoint = new PointF();
private final Rect mRepositionTaskBounds = new Rect();
- // If a task move (not resize) finishes in this region, the positioner will not attempt to
+ // If a task move (not resize) finishes with the positions y less than this value, do not
// finalize the bounds there using WCT#setBounds
- private final Rect mDisallowedAreaForEndBounds;
+ private final int mDisallowedAreaForEndBoundsHeight;
private boolean mHasDragResized;
private int mCtrlType;
FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
- DisplayController displayController, @Nullable Rect disallowedAreaForEndBounds) {
- this(taskOrganizer, windowDecoration, displayController, disallowedAreaForEndBounds,
- dragStartListener -> {}, SurfaceControl.Transaction::new);
+ DisplayController displayController, int disallowedAreaForEndBoundsHeight) {
+ this(taskOrganizer, windowDecoration, displayController, dragStartListener -> {},
+ SurfaceControl.Transaction::new, disallowedAreaForEndBoundsHeight);
}
FluidResizeTaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration,
- DisplayController displayController, @Nullable Rect disallowedAreaForEndBounds,
+ DisplayController displayController,
DragPositioningCallbackUtility.DragStartListener dragStartListener,
- Supplier<SurfaceControl.Transaction> supplier) {
+ Supplier<SurfaceControl.Transaction> supplier,
+ int disallowedAreaForEndBoundsHeight) {
mTaskOrganizer = taskOrganizer;
mWindowDecoration = windowDecoration;
mDisplayController = displayController;
- mDisallowedAreaForEndBounds = new Rect(disallowedAreaForEndBounds);
mDragStartListener = dragStartListener;
mTransactionSupplier = supplier;
+ mDisallowedAreaForEndBoundsHeight = disallowedAreaForEndBoundsHeight;
+ mDisplayController.getDisplayLayout(windowDecoration.mDisplay.getDisplayId())
+ .getStableBounds(mStableBounds);
}
@Override
@@ -121,10 +122,10 @@ class FluidResizeTaskPositioner implements DragPositioningCallback {
}
mTaskOrganizer.applyTransaction(wct);
} else if (mCtrlType == CTRL_TYPE_UNDEFINED
- && !mDisallowedAreaForEndBounds.contains((int) x, (int) y)) {
+ && y > mDisallowedAreaForEndBoundsHeight) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds,
- mTaskBoundsAtDragStart, mRepositionStartPoint, x, y);
+ DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds,
+ mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y);
wct.setBounds(mWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
mTaskOrganizer.applyTransaction(wct);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
index 82771095cd82..bfce72bcadf0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
@@ -102,11 +102,17 @@ public class ResizeVeil {
}
/**
- * Animate veil's alpha to 1, fading it in.
+ * Shows the veil surface/view.
+ *
+ * @param t the transaction to apply in sync with the veil draw
+ * @param parentSurface the surface that the veil should be a child of
+ * @param taskBounds the bounds of the task that owns the veil
+ * @param fadeIn if true, the veil will fade-in with an animation, if false, it will be shown
+ * immediately
*/
- public void showVeil(SurfaceControl parentSurface, Rect taskBounds) {
+ public void showVeil(SurfaceControl.Transaction t, SurfaceControl parentSurface,
+ Rect taskBounds, boolean fadeIn) {
// Parent surface can change, ensure it is up to date.
- SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
if (!parentSurface.equals(mParentSurface)) {
t.reparent(mVeilSurface, parentSurface);
mParentSurface = parentSurface;
@@ -115,22 +121,36 @@ public class ResizeVeil {
int backgroundColorId = getBackgroundColorId();
mViewHost.getView().setBackgroundColor(mContext.getColor(backgroundColorId));
- final ValueAnimator animator = new ValueAnimator();
- animator.setFloatValues(0f, 1f);
- animator.setDuration(RESIZE_ALPHA_DURATION);
- animator.addUpdateListener(animation -> {
- t.setAlpha(mVeilSurface, animator.getAnimatedFraction());
- t.apply();
- });
-
relayout(taskBounds, t);
- t.show(mVeilSurface)
- .addTransactionCommittedListener(mContext.getMainExecutor(), () -> animator.start())
- .setAlpha(mVeilSurface, 0);
+ if (fadeIn) {
+ final ValueAnimator animator = new ValueAnimator();
+ animator.setFloatValues(0f, 1f);
+ animator.setDuration(RESIZE_ALPHA_DURATION);
+ animator.addUpdateListener(animation -> {
+ t.setAlpha(mVeilSurface, animator.getAnimatedFraction());
+ t.apply();
+ });
+
+ t.show(mVeilSurface)
+ .addTransactionCommittedListener(
+ mContext.getMainExecutor(), () -> animator.start())
+ .setAlpha(mVeilSurface, 0);
+ } else {
+ // Show the veil immediately at full opacity.
+ t.show(mVeilSurface).setAlpha(mVeilSurface, 1);
+ }
mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
}
/**
+ * Animate veil's alpha to 1, fading it in.
+ */
+ public void showVeil(SurfaceControl parentSurface, Rect taskBounds) {
+ SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+ showVeil(t, parentSurface, taskBounds, true /* fadeIn */);
+ }
+
+ /**
* Update veil bounds to match bounds changes.
* @param newBounds bounds to update veil to.
*/
@@ -147,6 +167,16 @@ public class ResizeVeil {
*/
public void updateResizeVeil(Rect newBounds) {
SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+ updateResizeVeil(t, newBounds);
+ }
+
+ /**
+ * Calls relayout to update task and veil bounds.
+ *
+ * @param t a transaction to be applied in sync with the veil draw.
+ * @param newBounds bounds to update veil to.
+ */
+ public void updateResizeVeil(SurfaceControl.Transaction t, Rect newBounds) {
relayout(newBounds, t);
mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 58c78e6a5b9f..bf3ff3fa83ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -53,33 +53,35 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
private final Rect mTaskBoundsAtDragStart = new Rect();
private final PointF mRepositionStartPoint = new PointF();
private final Rect mRepositionTaskBounds = new Rect();
- // If a task move (not resize) finishes in this region, the positioner will not attempt to
+ // If a task move (not resize) finishes with the positions y less than this value, do not
// finalize the bounds there using WCT#setBounds
- private final Rect mDisallowedAreaForEndBounds = new Rect();
+ private final int mDisallowedAreaForEndBoundsHeight;
private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
private int mCtrlType;
public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
- Rect disallowedAreaForEndBounds,
DragPositioningCallbackUtility.DragStartListener dragStartListener,
- Transitions transitions) {
- this(taskOrganizer, windowDecoration, displayController, disallowedAreaForEndBounds,
- dragStartListener, SurfaceControl.Transaction::new, transitions);
+ Transitions transitions,
+ int disallowedAreaForEndBoundsHeight) {
+ this(taskOrganizer, windowDecoration, displayController, dragStartListener,
+ SurfaceControl.Transaction::new, transitions, disallowedAreaForEndBoundsHeight);
}
public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
DesktopModeWindowDecoration windowDecoration, DisplayController displayController,
- Rect disallowedAreaForEndBounds,
DragPositioningCallbackUtility.DragStartListener dragStartListener,
- Supplier<SurfaceControl.Transaction> supplier, Transitions transitions) {
+ Supplier<SurfaceControl.Transaction> supplier, Transitions transitions,
+ int disallowedAreaForEndBoundsHeight) {
mTaskOrganizer = taskOrganizer;
mDesktopWindowDecoration = windowDecoration;
mDisplayController = displayController;
mDragStartListener = dragStartListener;
- mDisallowedAreaForEndBounds.set(disallowedAreaForEndBounds);
mTransactionSupplier = supplier;
mTransitions = transitions;
+ mDisallowedAreaForEndBoundsHeight = disallowedAreaForEndBoundsHeight;
+ mDisplayController.getDisplayLayout(windowDecoration.mDisplay.getDisplayId())
+ .getStableBounds(mStableBounds);
}
@Override
@@ -110,8 +112,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
} else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
final SurfaceControl.Transaction t = mTransactionSupplier.get();
DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration,
- mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t,
- x, y);
+ mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t, x, y);
t.apply();
}
}
@@ -138,9 +139,9 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
// won't be called.
mDesktopWindowDecoration.hideResizeVeil();
}
- } else if (!mDisallowedAreaForEndBounds.contains((int) x, (int) y)) {
- DragPositioningCallbackUtility.updateTaskBounds(mRepositionTaskBounds,
- mTaskBoundsAtDragStart, mRepositionStartPoint, x, y);
+ } else if (y > mDisallowedAreaForEndBoundsHeight) {
+ DragPositioningCallbackUtility.onDragEnd(mRepositionTaskBounds,
+ mTaskBoundsAtDragStart, mStableBounds, mRepositionStartPoint, x, y);
DragPositioningCallbackUtility.applyTaskBoundsChange(new WindowContainerTransaction(),
mDesktopWindowDecoration, mRepositionTaskBounds, mTaskOrganizer);
}
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 9f03d9aec166..ae1a3d914be3 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
@@ -22,6 +22,7 @@ import android.view.SurfaceControl;
import android.window.TransitionInfo;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.splitscreen.SplitScreenController;
/**
* The interface used by some {@link com.android.wm.shell.ShellTaskOrganizer.TaskListener} to help
@@ -39,6 +40,11 @@ public interface WindowDecorViewModel {
void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter);
/**
+ * Sets the {@link SplitScreenController} if available.
+ */
+ void setSplitScreenController(SplitScreenController splitScreenController);
+
+ /**
* Creates a window decoration for the given task. Can be {@code null} for Fullscreen tasks but
* not Freeform ones.
*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index ac5ff2075901..ddc7fef0599f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -237,7 +237,12 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
final int captionWidth = taskBounds.width();
+
+ // We use mDecorationContainerSurface to define input window for task resizing; by layering
+ // it in front of mCaptionContainerSurface, we can allow it to handle input prior to
+ // caption view itself, treating corner inputs as resize events rather than repositioning.
startT.setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight)
+ .setLayer(mCaptionContainerSurface, -1)
.show(mCaptionContainerSurface);
if (ViewRootImpl.CAPTION_ON_SHELL) {
@@ -248,6 +253,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight + params.mCaptionY;
wct.addInsetsSource(mTaskInfo.token,
mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
+ wct.addInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
+ mCaptionInsetsRect);
} else {
startT.hide(mCaptionContainerSurface);
}
@@ -264,6 +272,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
.setColor(mTaskSurface, mTmpColor)
.show(mTaskSurface);
finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
+ .setShadowRadius(mTaskSurface, shadowRadius)
.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
@@ -301,6 +310,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
}
}
+ int getCaptionHeightId() {
+ return Resources.ID_NULL;
+ }
+
/**
* Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or
* registers {@link #mOnDisplaysChangedListener} if it doesn't.
@@ -345,6 +358,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
wct.removeInsetsSource(mTaskInfo.token,
mOwner, 0 /* index */, WindowInsets.Type.captionBar());
+ wct.removeInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures());
mTaskOrganizer.applyTransaction(wct);
}
@@ -413,6 +428,21 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mSurfaceControlTransactionSupplier);
}
+ /**
+ * Adds caption inset source to a WCT
+ */
+ public void addCaptionInset(WindowContainerTransaction wct) {
+ final int captionHeightId = getCaptionHeightId();
+ if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL) {
+ return;
+ }
+
+ final int captionHeight = loadDimensionPixelSize(mContext.getResources(), captionHeightId);
+ final Rect captionInsets = new Rect(0, 0, 0, captionHeight);
+ wct.addInsetsSource(mTaskInfo.token, mOwner, 0 /* index */, WindowInsets.Type.captionBar(),
+ captionInsets);
+ }
+
static class RelayoutParams {
RunningTaskInfo mRunningTaskInfo;
int mLayoutResId;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
index 514ea52cb8ae..d293cf73a0f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
@@ -1,28 +1,35 @@
package com.android.wm.shell.windowdecor.viewholder
import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.Context
import android.graphics.Color
import android.view.View
+import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
/**
* Encapsulates the root [View] of a window decoration and its children to facilitate looking up
* children (via findViewById) and updating to the latest data from [RunningTaskInfo].
*/
internal abstract class DesktopModeWindowDecorationViewHolder(rootView: View) {
- val context: Context = rootView.context
+ val context: Context = rootView.context
- /**
- * A signal to the view holder that new data is available and that the views should be updated
- * to reflect it.
- */
- abstract fun bindData(taskInfo: RunningTaskInfo)
+ /**
+ * A signal to the view holder that new data is available and that the views should be updated to
+ * reflect it.
+ */
+ abstract fun bindData(taskInfo: RunningTaskInfo)
- /**
- * Whether the caption items should use the 'light' color variant so that there's good contrast
- * with the caption background color.
- */
- protected fun shouldUseLightCaptionColors(taskInfo: RunningTaskInfo): Boolean {
- return Color.valueOf(taskInfo.taskDescription.statusBarColor).luminance() < 0.5
+ /**
+ * Whether the caption items should use the 'light' color variant so that there's good contrast
+ * with the caption background color.
+ */
+ protected fun shouldUseLightCaptionColors(taskInfo: RunningTaskInfo): Boolean {
+ return if (Color.alpha(taskInfo.taskDescription.statusBarColor) != 0 &&
+ taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
+ Color.valueOf(taskInfo.taskDescription.statusBarColor).luminance() < 0.5
+ } else {
+ taskInfo.taskDescription.statusBarAppearance and APPEARANCE_LIGHT_STATUS_BARS == 0
}
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index b6696c70dbb1..e382a0f0aeda 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -23,14 +23,33 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-android_test {
- name: "WMShellFlickerTests",
+filegroup {
+ name: "WMShellFlickerTestsBase-src",
+ srcs: ["src/com/android/wm/shell/flicker/*.kt"],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsBubbles-src",
+ srcs: ["src/**/bubble/*.kt"],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsPip-src",
+ srcs: ["src/**/pip/*.kt"],
+}
+
+filegroup {
+ name: "WMShellFlickerTestsSplitScreen-src",
srcs: [
- "src/**/*.java",
- "src/**/*.kt",
+ "src/**/splitscreen/*.kt",
+ "src/**/splitscreen/benchmark/*.kt",
],
- manifest: "AndroidManifest.xml",
- test_config: "AndroidTest.xml",
+}
+
+java_defaults {
+ name: "WMShellFlickerTestsDefault",
+ manifest: "manifests/AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
platform_apis: true,
certificate: "platform",
optimize: {
@@ -40,16 +59,70 @@ android_test {
libs: ["android.test.runner"],
static_libs: [
"androidx.test.ext.junit",
+ "flickertestapplib",
"flickerlib",
- "flickerlib-apphelpers",
"flickerlib-helpers",
- "truth-prebuilt",
- "app-helpers-core",
+ "platform-test-annotations",
+ "wm-flicker-common-app-helpers",
+ "wm-flicker-common-assertions",
"launcher-helper-lib",
"launcher-aosp-tapl",
- "wm-flicker-common-assertions",
- "wm-flicker-common-app-helpers",
- "platform-test-annotations",
- "flickertestapplib",
+ ],
+ data: [
+ ":FlickerTestApp",
+ "trace_config/*",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsOther",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestOther.xml"],
+ package_name: "com.android.wm.shell.flicker",
+ instrumentation_target_package: "com.android.wm.shell.flicker",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ exclude_srcs: [
+ ":WMShellFlickerTestsBubbles-src",
+ ":WMShellFlickerTestsPip-src",
+ ":WMShellFlickerTestsSplitScreen-src",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsBubbles",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestBubbles.xml"],
+ package_name: "com.android.wm.shell.flicker.bubbles",
+ instrumentation_target_package: "com.android.wm.shell.flicker.bubbles",
+ srcs: [
+ ":WMShellFlickerTestsBase-src",
+ ":WMShellFlickerTestsBubbles-src",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsPip",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestPip.xml"],
+ package_name: "com.android.wm.shell.flicker.pip",
+ instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+ srcs: [
+ ":WMShellFlickerTestsBase-src",
+ ":WMShellFlickerTestsPip-src",
+ ],
+}
+
+android_test {
+ name: "WMShellFlickerTestsSplitScreen",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"],
+ package_name: "com.android.wm.shell.flicker.splitscreen",
+ instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+ srcs: [
+ ":WMShellFlickerTestsBase-src",
+ ":WMShellFlickerTestsSplitScreen-src",
],
}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
deleted file mode 100644
index b5937ae80f0a..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright 2020 Google Inc. All Rights Reserved.
- -->
-<configuration description="Runs WindowManager Shell Flicker Tests">
- <option name="test-tag" value="FlickerTests" />
- <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
- <!-- keeps the screen on during tests -->
- <option name="screen-always-on" value="on" />
- <!-- prevents the phone from restarting -->
- <option name="force-skip-system-props" value="true" />
- <!-- set WM tracing verbose level to all -->
- <option name="run-command" value="cmd window tracing level all" />
- <!-- set WM tracing to frame (avoid incomplete states) -->
- <option name="run-command" value="cmd window tracing frame" />
- <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
- <option name="run-command" value="pm disable com.google.android.internal.betterbug" />
- <!-- ensure lock screen mode is swipe -->
- <option name="run-command" value="locksettings set-disabled false" />
- <!-- restart launcher to activate TAPL -->
- <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
- <!-- Ensure output directory is empty at the start -->
- <option name="run-command" value="rm -rf /sdcard/flicker" />
- <!-- Increase trace size: 20mb for WM and 80mb for SF -->
- <option name="run-command" value="cmd window tracing size 20480" />
- <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
- <option name="run-command" value="settings put system show_touches 1" />
- <option name="run-command" value="settings put system pointer_location 1" />
- <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" />
- <option name="teardown-command" value="settings delete system show_touches" />
- <option name="teardown-command" value="settings delete system pointer_location" />
- <option name="teardown-command" value="cmd overlay enable com.android.internal.systemui.navbar.gestural" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true"/>
- <option name="test-file-name" value="WMShellFlickerTests.apk"/>
- <option name="test-file-name" value="FlickerTestApp.apk" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="com.android.wm.shell.flicker"/>
- <option name="shell-timeout" value="6600s" />
- <option name="test-timeout" value="6000s" />
- <option name="hidden-api-checks" value="false" />
- </test>
- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/sdcard/flicker" />
- <option name="collect-on-run-ended-only" value="true" />
- <option name="clean-up" value="true" />
- </metrics_collector>
-</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
new file mode 100644
index 000000000000..991d7b5480c4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6000s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.pip/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.splitscreen/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
index 4721741611cf..6a87de47def4 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
@@ -15,6 +15,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.android.wm.shell.flicker">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
@@ -57,10 +58,11 @@
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
- </application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker"
- android:label="WindowManager Shell Flicker Tests">
- </instrumentation>
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml
new file mode 100644
index 000000000000..437871f1bb8f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.wm.shell.flicker.bubble">
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.wm.shell.flicker.bubble"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml
new file mode 100644
index 000000000000..cf642f63a41d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.wm.shell.flicker">
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.wm.shell.flicker"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml
new file mode 100644
index 000000000000..5a8155a66d30
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.wm.shell.flicker.pip">
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.wm.shell.flicker.pip"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml
new file mode 100644
index 000000000000..887d8db3042f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.wm.shell.flicker.splitscreen">
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.wm.shell.flicker.splitscreen"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
index e06e074ee98a..0f3e0f5ef043 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
@@ -19,14 +19,14 @@ package com.android.wm.shell.flicker
import android.app.Instrumentation
import android.tools.device.flicker.junit.FlickerBuilderProvider
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
abstract class BaseBenchmarkTest
@JvmOverloads
constructor(
- protected open val flicker: FlickerTest,
+ protected open val flicker: LegacyFlickerTest,
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
protected val tapl: LauncherInstrumentation = LauncherInstrumentation()
) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index c98c5a0ad1a6..d2fe9fec460c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -18,7 +18,7 @@ package com.android.wm.shell.flicker
import android.app.Instrumentation
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -30,7 +30,7 @@ import com.android.launcher3.tapl.LauncherInstrumentation
abstract class BaseTest
@JvmOverloads
constructor(
- override val flicker: FlickerTest,
+ override val flicker: LegacyFlickerTest,
instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
tapl: LauncherInstrumentation = LauncherInstrumentation()
) : BaseBenchmarkTest(flicker, instrumentation, tapl), ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 798cc95c020f..9cc03a56e9a2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -23,18 +23,18 @@ import android.tools.common.datatypes.Region
import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject
import android.tools.common.flicker.subject.layers.LayersTraceSubject
import android.tools.common.traces.component.IComponentMatcher
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.helpers.WindowUtils
-fun FlickerTest.appPairsDividerIsVisibleAtEnd() {
+fun LegacyFlickerTest.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT) }
}
-fun FlickerTest.appPairsDividerIsInvisibleAtEnd() {
+fun LegacyFlickerTest.appPairsDividerIsInvisibleAtEnd() {
assertLayersEnd { this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT) }
}
-fun FlickerTest.appPairsDividerBecomesVisible() {
+fun LegacyFlickerTest.appPairsDividerBecomesVisible() {
assertLayers {
this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
@@ -42,7 +42,7 @@ fun FlickerTest.appPairsDividerBecomesVisible() {
}
}
-fun FlickerTest.splitScreenEntered(
+fun LegacyFlickerTest.splitScreenEntered(
component1: IComponentMatcher,
component2: IComponentMatcher,
fromOtherApp: Boolean,
@@ -69,7 +69,7 @@ fun FlickerTest.splitScreenEntered(
splitScreenDividerIsVisibleAtEnd()
}
-fun FlickerTest.splitScreenDismissed(
+fun LegacyFlickerTest.splitScreenDismissed(
component1: IComponentMatcher,
component2: IComponentMatcher,
toHome: Boolean
@@ -87,27 +87,27 @@ fun FlickerTest.splitScreenDismissed(
splitScreenDividerIsInvisibleAtEnd()
}
-fun FlickerTest.splitScreenDividerIsVisibleAtStart() {
+fun LegacyFlickerTest.splitScreenDividerIsVisibleAtStart() {
assertLayersStart { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
}
-fun FlickerTest.splitScreenDividerIsVisibleAtEnd() {
+fun LegacyFlickerTest.splitScreenDividerIsVisibleAtEnd() {
assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
}
-fun FlickerTest.splitScreenDividerIsInvisibleAtStart() {
+fun LegacyFlickerTest.splitScreenDividerIsInvisibleAtStart() {
assertLayersStart { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
}
-fun FlickerTest.splitScreenDividerIsInvisibleAtEnd() {
+fun LegacyFlickerTest.splitScreenDividerIsInvisibleAtEnd() {
assertLayersEnd { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
}
-fun FlickerTest.splitScreenDividerBecomesVisible() {
+fun LegacyFlickerTest.splitScreenDividerBecomesVisible() {
layerBecomesVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
}
-fun FlickerTest.splitScreenDividerBecomesInvisible() {
+fun LegacyFlickerTest.splitScreenDividerBecomesInvisible() {
assertLayers {
this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
.then()
@@ -115,23 +115,23 @@ fun FlickerTest.splitScreenDividerBecomesInvisible() {
}
}
-fun FlickerTest.layerBecomesVisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.layerBecomesVisible(component: IComponentMatcher) {
assertLayers { this.isInvisible(component).then().isVisible(component) }
}
-fun FlickerTest.layerBecomesInvisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.layerBecomesInvisible(component: IComponentMatcher) {
assertLayers { this.isVisible(component).then().isInvisible(component) }
}
-fun FlickerTest.layerIsVisibleAtEnd(component: IComponentMatcher) {
+fun LegacyFlickerTest.layerIsVisibleAtEnd(component: IComponentMatcher) {
assertLayersEnd { this.isVisible(component) }
}
-fun FlickerTest.layerKeepVisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.layerKeepVisible(component: IComponentMatcher) {
assertLayers { this.isVisible(component) }
}
-fun FlickerTest.splitAppLayerBoundsBecomesVisible(
+fun LegacyFlickerTest.splitAppLayerBoundsBecomesVisible(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean
@@ -150,7 +150,7 @@ fun FlickerTest.splitAppLayerBoundsBecomesVisible(
}
}
-fun FlickerTest.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMatcher) {
+fun LegacyFlickerTest.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMatcher) {
assertLayers {
this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true)
.then()
@@ -161,7 +161,7 @@ fun FlickerTest.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMat
}
}
-fun FlickerTest.splitAppLayerBoundsBecomesInvisible(
+fun LegacyFlickerTest.splitAppLayerBoundsBecomesInvisible(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean
@@ -180,7 +180,7 @@ fun FlickerTest.splitAppLayerBoundsBecomesInvisible(
}
}
-fun FlickerTest.splitAppLayerBoundsIsVisibleAtEnd(
+fun LegacyFlickerTest.splitAppLayerBoundsIsVisibleAtEnd(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean
@@ -195,7 +195,7 @@ fun FlickerTest.splitAppLayerBoundsIsVisibleAtEnd(
}
}
-fun FlickerTest.splitAppLayerBoundsKeepVisible(
+fun LegacyFlickerTest.splitAppLayerBoundsKeepVisible(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean
@@ -210,7 +210,7 @@ fun FlickerTest.splitAppLayerBoundsKeepVisible(
}
}
-fun FlickerTest.splitAppLayerBoundsChanges(
+fun LegacyFlickerTest.splitAppLayerBoundsChanges(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean
@@ -304,7 +304,7 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
}
}
-fun FlickerTest.appWindowBecomesVisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowBecomesVisible(component: IComponentMatcher) {
assertWm {
this.isAppWindowInvisible(component)
.then()
@@ -316,39 +316,39 @@ fun FlickerTest.appWindowBecomesVisible(component: IComponentMatcher) {
}
}
-fun FlickerTest.appWindowBecomesInvisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowBecomesInvisible(component: IComponentMatcher) {
assertWm { this.isAppWindowVisible(component).then().isAppWindowInvisible(component) }
}
-fun FlickerTest.appWindowIsVisibleAtStart(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowIsVisibleAtStart(component: IComponentMatcher) {
assertWmStart { this.isAppWindowVisible(component) }
}
-fun FlickerTest.appWindowIsVisibleAtEnd(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowIsVisibleAtEnd(component: IComponentMatcher) {
assertWmEnd { this.isAppWindowVisible(component) }
}
-fun FlickerTest.appWindowIsInvisibleAtStart(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowIsInvisibleAtStart(component: IComponentMatcher) {
assertWmStart { this.isAppWindowInvisible(component) }
}
-fun FlickerTest.appWindowIsInvisibleAtEnd(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowIsInvisibleAtEnd(component: IComponentMatcher) {
assertWmEnd { this.isAppWindowInvisible(component) }
}
-fun FlickerTest.appWindowIsNotContainAtStart(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowIsNotContainAtStart(component: IComponentMatcher) {
assertWmStart { this.notContains(component) }
}
-fun FlickerTest.appWindowKeepVisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowKeepVisible(component: IComponentMatcher) {
assertWm { this.isAppWindowVisible(component) }
}
-fun FlickerTest.dockedStackDividerIsVisibleAtEnd() {
+fun LegacyFlickerTest.dockedStackDividerIsVisibleAtEnd() {
assertLayersEnd { this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) }
}
-fun FlickerTest.dockedStackDividerBecomesVisible() {
+fun LegacyFlickerTest.dockedStackDividerBecomesVisible() {
assertLayers {
this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
@@ -356,7 +356,7 @@ fun FlickerTest.dockedStackDividerBecomesVisible() {
}
}
-fun FlickerTest.dockedStackDividerBecomesInvisible() {
+fun LegacyFlickerTest.dockedStackDividerBecomesInvisible() {
assertLayers {
this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
@@ -364,11 +364,11 @@ fun FlickerTest.dockedStackDividerBecomesInvisible() {
}
}
-fun FlickerTest.dockedStackDividerNotExistsAtEnd() {
+fun LegacyFlickerTest.dockedStackDividerNotExistsAtEnd() {
assertLayersEnd { this.notContains(DOCKED_STACK_DIVIDER_COMPONENT) }
}
-fun FlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd(
+fun LegacyFlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd(
rotation: Rotation,
primaryComponent: IComponentMatcher
) {
@@ -380,7 +380,7 @@ fun FlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd(
}
}
-fun FlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd(
+fun LegacyFlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd(
rotation: Rotation,
primaryComponent: IComponentMatcher
) {
@@ -392,7 +392,7 @@ fun FlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd(
}
}
-fun FlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd(
+fun LegacyFlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd(
rotation: Rotation,
secondaryComponent: IComponentMatcher
) {
@@ -404,7 +404,7 @@ fun FlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd(
}
}
-fun FlickerTest.dockedStackSecondaryBoundsIsVisibleAtEnd(
+fun LegacyFlickerTest.dockedStackSecondaryBoundsIsVisibleAtEnd(
rotation: Rotation,
secondaryComponent: IComponentMatcher
) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt
index 02d9a056afbf..7b3290125bae 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt
@@ -18,7 +18,7 @@ package com.android.wm.shell.flicker
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
@@ -32,7 +32,7 @@ import org.junit.Assume
import org.junit.Test
interface ICommonAssertions {
- val flicker: FlickerTest
+ val flicker: LegacyFlickerTest
/** Checks that all parts of the screen are covered during the transition */
@Presubmit @Test fun entireScreenCovered() = flicker.entireScreenCovered()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
index 61781565270b..0f9579d58929 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -20,21 +20,20 @@ import android.content.Context
import android.system.helpers.CommandsHelper
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import com.android.server.wm.flicker.helpers.setRotation
+import android.tools.device.flicker.legacy.FlickerTestData
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.helpers.LetterboxAppHelper
-import android.tools.device.flicker.legacy.FlickerTestFactory
-import android.tools.device.flicker.legacy.IFlickerTestData
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.BaseTest
-import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
+import com.android.wm.shell.flicker.appWindowKeepVisible
import com.android.wm.shell.flicker.layerKeepVisible
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.runners.Parameterized
-abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected val context: Context = instrumentation.context
protected val letterboxApp = LetterboxAppHelper(instrumentation)
lateinit var cmdHelper: CommandsHelper
@@ -48,9 +47,7 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) {
letterboxApp.launchViaIntent(wmHelper)
setEndRotation()
}
- teardown {
- letterboxApp.exit(wmHelper)
- }
+ teardown { letterboxApp.exit(wmHelper) }
}
@Before
@@ -58,12 +55,13 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) {
cmdHelper = CommandsHelper.getInstance(instrumentation)
Assume.assumeTrue(tapl.isTablet && isIgnoreOrientationRequest())
letterboxStyle = mapLetterboxStyle()
+ resetLetterboxStyle()
setLetterboxEducationEnabled(false)
}
@After
fun after() {
- resetLetterboxEducationEnabled()
+ resetLetterboxStyle()
}
private fun mapLetterboxStyle(): HashMap<String, String> {
@@ -87,9 +85,8 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) {
return letterboxStyle
}
- private fun resetLetterboxEducationEnabled() {
- val enabled = getLetterboxStyle().getValue("Is education enabled")
- cmdHelper.executeShellCommand("wm set-letterbox-style --isEducationEnabled $enabled")
+ private fun resetLetterboxStyle() {
+ cmdHelper.executeShellCommand("wm reset-letterbox-style")
}
private fun setLetterboxEducationEnabled(enabled: Boolean) {
@@ -101,9 +98,9 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) {
return res != null && res.contains("true")
}
- fun IFlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation)
+ fun FlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation)
- fun IFlickerTestData.setEndRotation() = setRotation(flicker.scenario.endRotation)
+ fun FlickerTestData.setEndRotation() = setRotation(flicker.scenario.endRotation)
/** Checks that app entering letterboxed state have rounded corners */
fun assertLetterboxAppAtStartHasRoundedCorners() {
@@ -126,25 +123,21 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) {
flicker.appWindowIsVisibleAtEnd(letterboxApp)
}
+ fun assertLetterboxAppKeepVisible() {
+ assertLetterboxAppWindowKeepVisible()
+ assertLetterboxAppLayerKeepVisible()
+ }
+
fun assertAppLetterboxedAtEnd() =
- flicker.assertLayersEnd { isVisible(ComponentNameMatcher.LETTERBOX) }
+ flicker.assertLayersEnd { isVisible(ComponentNameMatcher.LETTERBOX) }
fun assertAppLetterboxedAtStart() =
- flicker.assertLayersStart { isVisible(ComponentNameMatcher.LETTERBOX) }
+ flicker.assertLayersStart { isVisible(ComponentNameMatcher.LETTERBOX) }
+
+ fun assertAppStaysLetterboxed() =
+ flicker.assertLayers { isVisible(ComponentNameMatcher.LETTERBOX) }
fun assertLetterboxAppLayerKeepVisible() = flicker.layerKeepVisible(letterboxApp)
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [FlickerTestFactory.rotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.rotationTests()
- }
- }
+ fun assertLetterboxAppWindowKeepVisible() = flicker.appWindowKeepVisible(letterboxApp)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
index c2141a370f10..a7bd2584ba23 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -17,10 +17,12 @@
package com.android.wm.shell.flicker.appcompat
import android.platform.test.annotations.Postsubmit
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,7 +47,7 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) {
+class OpenAppInSizeCompatModeTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -70,9 +72,7 @@ class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker)
@Test
fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners()
- @Postsubmit
- @Test
- fun appIsLetterboxedAtEnd() = assertAppLetterboxedAtEnd()
+ @Postsubmit @Test fun appIsLetterboxedAtEnd() = assertAppLetterboxedAtEnd()
/**
* Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't
@@ -90,4 +90,18 @@ class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker)
.isInvisible(ComponentNameMatcher.ROTATION)
}
}
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.rotationTests()
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
new file mode 100644
index 000000000000..6fe88cacbbc7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.tools.common.flicker.assertions.FlickerTest
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.datatypes.Rect
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switching to letterboxed app from launcher
+ *
+ * To run this test: `atest WMShellFlickerTestsOther:QuickSwitchLauncherToLetterboxAppTest`
+ *
+ * Actions:
+ * ```
+ * Launch a letterboxed app
+ * Navigate home to show launcher
+ * Swipe right from the bottom of the screen to quick switch back to the app
+ * ```
+ */
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class QuickSwitchLauncherToLetterboxAppTest(flicker: LegacyFlickerTest) :
+ BaseAppCompat(flicker) {
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+
+ letterboxApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withHomeActivityVisible()
+ .withWindowSurfaceDisappeared(letterboxApp)
+ .waitForAndVerify()
+
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
+ }
+ transitions {
+ tapl.workspace.quickSwitchToPreviousApp()
+ wmHelper
+ .StateSyncBuilder()
+ .withFullScreenApp(letterboxApp)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
+ }
+ teardown { letterboxApp.exit(wmHelper) }
+ }
+
+ /**
+ * Checks that [letterboxApp] is the top window at the end of the transition once we have fully
+ * quick switched from the launcher back to the [letterboxApp].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithAppBeingOnTop() {
+ flicker.assertWmEnd { this.isAppWindowOnTop(letterboxApp) }
+ }
+
+ /** Checks that the transition starts with the home activity being tagged as visible. */
+ @Postsubmit
+ @Test
+ fun startsWithHomeActivityFlaggedVisible() {
+ flicker.assertWmStart { this.isHomeActivityVisible() }
+ }
+
+ /**
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] windows
+ * filling/covering exactly display size
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherWindowsCoverFullScreen() {
+ flicker.assertWmStart {
+ this.visibleRegion(ComponentNameMatcher.LAUNCHER).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] layers
+ * filling/covering exactly the display size.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherLayersCoverFullScreen() {
+ flicker.assertLayersStart {
+ this.visibleRegion(ComponentNameMatcher.LAUNCHER).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] being the top
+ * window.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherBeingOnTop() {
+ flicker.assertWmStart { this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER) }
+ }
+
+ /**
+ * Checks that the transition ends with the home activity being flagged as not visible. By this
+ * point we should have quick switched away from the launcher back to the [letterboxApp].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithHomeActivityFlaggedInvisible() {
+ flicker.assertWmEnd { this.isHomeActivityInvisible() }
+ }
+
+ /**
+ * Checks that [letterboxApp]'s window starts off invisible and becomes visible at some point
+ * before the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun appWindowBecomesAndStaysVisible() {
+ flicker.assertWm {
+ this.isAppWindowInvisible(letterboxApp)
+ .then()
+ .isAppWindowVisible(letterboxApp) }
+ }
+
+ /**
+ * Checks that [letterboxApp]'s layer starts off invisible and becomes visible at some point
+ * before the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun appLayerBecomesAndStaysVisible() {
+ flicker.assertLayers { this.isInvisible(letterboxApp).then().isVisible(letterboxApp) }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] window starts off visible and becomes
+ * invisible at some point before the end of the transition and then stays invisible until the
+ * end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun launcherWindowBecomesAndStaysInvisible() {
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer starts off visible and becomes
+ * invisible at some point before the end of the transition and then stays invisible until the
+ * end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun launcherLayerBecomesAndStaysInvisible() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isInvisible(ComponentNameMatcher.LAUNCHER)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] window is visible at least until the app
+ * window is visible. Ensures that at any point, either the launcher or [letterboxApp] windows
+ * are at least partially visible.
+ */
+ @Postsubmit
+ @Test
+ fun appWindowIsVisibleOnceLauncherWindowIsInvisible() {
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(letterboxApp)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer is visible at least until the app layer
+ * is visible. Ensures that at any point, either the launcher or [letterboxApp] layers are at
+ * least partially visible.
+ */
+ @Postsubmit
+ @Test
+ fun appLayerIsVisibleOnceLauncherLayerIsInvisible() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(letterboxApp)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LETTERBOX] layer is visible as soon as the
+ * [letterboxApp] layer is visible at the end of the transition once we have fully quick
+ * switched from the launcher back to the [letterboxApp].
+ */
+ @Postsubmit
+ @Test
+ fun appAndLetterboxLayersBothVisibleOnceLauncherIsInvisible() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(letterboxApp)
+ .isVisible(ComponentNameMatcher.LETTERBOX) }
+ }
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
+ supportedRotations = listOf(Rotation.ROTATION_90)
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
new file mode 100644
index 000000000000..e875aae431a1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.device.helpers.WindowUtils
+import androidx.test.filters.RequiresDevice
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a fixed portrait letterboxed app in landscape and repositioning to the right.
+ *
+ * To run this test: `atest WMShellFlickerTests:RepositionFixedPortraitAppTest` Actions:
+ *
+ * ```
+ * Launch a fixed portrait app in landscape to letterbox app
+ * Double tap to the right to reposition app and wait for app to move
+ * ```
+ *
+ * Notes:
+ *
+ * ```
+ * Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [BaseTest]
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class RepositionFixedPortraitAppTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
+
+ val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation).bounds
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ setStartRotation()
+ letterboxApp.launchViaIntent(wmHelper)
+ }
+ transitions {
+ letterboxApp.repositionHorizontally(displayBounds, true)
+ letterboxApp.waitForAppToMoveHorizontallyTo(wmHelper, displayBounds, true)
+ }
+ teardown {
+ letterboxApp.repositionHorizontally(displayBounds, false)
+ letterboxApp.exit(wmHelper)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners()
+
+ @Postsubmit @Test fun letterboxAppLayerKeepVisible() = assertLetterboxAppLayerKeepVisible()
+
+ @Postsubmit @Test fun appStaysLetterboxed() = assertAppStaysLetterboxed()
+
+ @Postsubmit @Test fun appKeepVisible() = assertLetterboxAppKeepVisible()
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_90)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
index b0e1a42306df..a18a144b4bf1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
@@ -17,9 +17,11 @@
package com.android.wm.shell.flicker.appcompat
import android.platform.test.annotations.Postsubmit
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import org.junit.Test
@@ -46,7 +48,7 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) {
+class RestartAppInSizeCompatModeTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -69,13 +71,9 @@ class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flick
}
}
- @Postsubmit
- @Test
- fun appLayerKeepVisible() = assertLetterboxAppLayerKeepVisible()
+ @Postsubmit @Test fun appLayerKeepVisible() = assertLetterboxAppLayerKeepVisible()
- @Postsubmit
- @Test
- fun appIsLetterboxedAtStart() = assertAppLetterboxedAtStart()
+ @Postsubmit @Test fun appIsLetterboxedAtStart() = assertAppLetterboxedAtStart()
@Postsubmit
@Test
@@ -88,4 +86,18 @@ class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flick
val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
flicker.assertLayersEnd { visibleRegion(letterboxApp).coversAtMost(displayBounds) }
}
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.rotationTests()
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index bab81d79c804..5c7d1d8df2e8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -23,9 +23,9 @@ import android.content.pm.PackageManager
import android.os.ServiceManager
import android.tools.common.Rotation
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
-import android.tools.device.flicker.legacy.IFlickerTestData
+import android.tools.device.flicker.legacy.FlickerTestData
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.SYSTEMUI_PACKAGE
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
@@ -35,7 +35,7 @@ import com.android.wm.shell.flicker.BaseTest
import org.junit.runners.Parameterized
/** Base configurations for Bubble flicker tests */
-abstract class BaseBubbleScreen(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected val context: Context = instrumentation.context
protected val testApp = LaunchBubbleHelper(instrumentation)
@@ -72,28 +72,31 @@ abstract class BaseBubbleScreen(flicker: FlickerTest) : BaseTest(flicker) {
uid,
NotificationManager.BUBBLE_PREFERENCE_NONE
)
- testApp.exit()
+ device.wait(
+ Until.gone(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)),
+ FIND_OBJECT_TIMEOUT
+ )
+ testApp.exit(wmHelper)
}
extraSpec(this)
}
}
- protected fun IFlickerTestData.waitAndGetAddBubbleBtn(): UiObject2? =
+ protected fun FlickerTestData.waitAndGetAddBubbleBtn(): UiObject2? =
device.wait(Until.findObject(By.text("Add Bubble")), FIND_OBJECT_TIMEOUT)
- protected fun IFlickerTestData.waitAndGetCancelAllBtn(): UiObject2? =
+ protected fun FlickerTestData.waitAndGetCancelAllBtn(): UiObject2? =
device.wait(Until.findObject(By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT)
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
- const val FIND_OBJECT_TIMEOUT = 2000L
+ const val FIND_OBJECT_TIMEOUT = 4000L
const val SYSTEM_UI_PACKAGE = SYSTEMUI_PACKAGE
const val BUBBLE_RES_NAME = "bubble_view"
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
index 2474ecf74cf9..bc565bc5fd42 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
@@ -21,7 +21,7 @@ import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
@@ -44,7 +44,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FlakyTest(bugId = 217777115)
-open class ChangeActiveActivityFromBubbleTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+open class ChangeActiveActivityFromBubbleTest(flicker: LegacyFlickerTest) :
+ BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
index bdfdad59c600..abc6b9f9a746 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.bubble
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class ChangeActiveActivityFromBubbleTestCfArm(flicker: FlickerTest) :
+open class ChangeActiveActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) :
ChangeActiveActivityFromBubbleTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
index 8474ce0e64e5..3f28ae848d1f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
@@ -19,9 +19,10 @@ package com.android.wm.shell.flicker.bubble
import android.content.Context
import android.graphics.Point
import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
@@ -44,7 +45,7 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class DragToDismissBubbleScreenTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+open class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
private val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private val displaySize = DisplayMetrics()
@@ -73,4 +74,14 @@ open class DragToDismissBubbleScreenTest(flicker: FlickerTest) : BaseBubbleScree
open fun testAppIsAlwaysVisible() {
flicker.assertLayers { this.isVisible(testApp) }
}
+
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(testApp)
+ )
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt
index 62fa7b4516c7..ee55eca31072 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.bubble
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class DragToDismissBubbleScreenTestCfArm(flicker: FlickerTest) :
+class DragToDismissBubbleScreenTestCfArm(flicker: LegacyFlickerTest) :
DragToDismissBubbleScreenTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 889e1771593d..26aca1830889 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -21,7 +21,7 @@ import android.platform.test.annotations.Postsubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.view.WindowInsets
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
@@ -48,7 +48,8 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class OpenActivityFromBubbleOnLocksreenTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+class OpenActivityFromBubbleOnLocksreenTest(flicker: LegacyFlickerTest) :
+ BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -80,7 +81,7 @@ class OpenActivityFromBubbleOnLocksreenTest(flicker: FlickerTest) : BaseBubbleSc
instrumentation.uiAutomation.syncInputTransactions()
val showBubble =
device.wait(
- Until.findObject(By.res("com.android.systemui", "bubble_view")),
+ Until.findObject(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)),
FIND_OBJECT_TIMEOUT
)
showBubble?.click() ?: error("Bubble notify not found")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
index 07ba41333071..508539411aa0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.bubble
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
@@ -42,7 +42,7 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class OpenActivityFromBubbleTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+open class OpenActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt
index 6c61710d6284..6a46d23ad2a1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt
@@ -17,10 +17,11 @@
package com.android.wm.shell.flicker.bubble
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class OpenActivityFromBubbleTestCfArm(flicker: FlickerTest) : OpenActivityFromBubbleTest(flicker)
+class OpenActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) :
+ OpenActivityFromBubbleTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
index 29f76d01af83..a926bb7d85c3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.bubble
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
@@ -41,7 +41,7 @@ import org.junit.runners.Parameterized
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class SendBubbleNotificationTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+open class SendBubbleNotificationTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -55,6 +55,7 @@ open class SendBubbleNotificationTest(flicker: FlickerTest) : BaseBubbleScreen(f
FIND_OBJECT_TIMEOUT
)
?: error("No bubbles found")
+ device.waitForIdle()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt
index e323ebf3b5c8..a401cb494822 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.bubble
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class SendBubbleNotificationTestCfArm(flicker: FlickerTest) :
+open class SendBubbleNotificationTestCfArm(flicker: LegacyFlickerTest) :
SendBubbleNotificationTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index f7ce87088040..36bbafb4c05e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -24,8 +24,8 @@ import android.tools.common.Rotation
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
@@ -69,17 +69,17 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) :
- AutoEnterPipOnGoToHomeTest(flicker) {
+class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) :
+ AutoEnterPipOnGoToHomeTest(flicker) {
private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
/** Second app used to enter split screen mode */
protected val secondAppForSplitScreen = getSplitScreenApp(instrumentation)
fun getSplitScreenApp(instrumentation: Instrumentation): StandardAppHelper =
- SimpleAppHelper(
- instrumentation,
- ActivityOptions.SplitScreen.Primary.LABEL,
- ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
- )
+ SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Primary.LABEL,
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
+ )
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
@@ -91,11 +91,11 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) :
enterSplitScreen()
// wait until split screen is established
wmHelper
- .StateSyncBuilder()
- .withWindowSurfaceAppeared(pipApp)
- .withWindowSurfaceAppeared(secondAppForSplitScreen)
- .withSplitDividerVisible()
- .waitForAndVerify()
+ .StateSyncBuilder()
+ .withWindowSurfaceAppeared(pipApp)
+ .withWindowSurfaceAppeared(secondAppForSplitScreen)
+ .withSplitDividerVisible()
+ .waitForAndVerify()
pipApp.enableAutoEnterForPipActivity()
}
teardown {
@@ -120,8 +120,8 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) :
// contains more than 3 task views. We need to use uiautomator directly to find the
// second task to split.
tapl.workspace.switchToOverview().overviewActions.clickSplit()
- val snapshots = tapl.device.wait(Until.findObjects(overviewSnapshotSelector),
- TIMEOUT_MS)
+ val snapshots =
+ tapl.device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
if (snapshots == null || snapshots.size < 1) {
error("Fail to find a overview snapshot to split.")
}
@@ -137,12 +137,12 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) :
snapshots[0].click()
} else {
tapl.workspace
- .switchToOverview()
- .currentTask
- .tapMenu()
- .tapSplitMenuItem()
- .currentTask
- .open()
+ .switchToOverview()
+ .currentTask
+ .tapMenu()
+ .tapSplitMenuItem()
+ .currentTask
+ .open()
}
SystemClock.sleep(TIMEOUT_MS)
}
@@ -190,11 +190,10 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
- // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index b95732e43357..2f7a25ea586d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -53,10 +53,9 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) {
- override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions { tapl.goHome() }
- }
+open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
+ EnterPipViaAppUiButtonTest(flicker) {
+ override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index afcc1729ed16..68bc9a28967e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -20,7 +20,7 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -53,7 +53,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ClosePipBySwipingDownTest(flicker: FlickerTest) : ClosePipTransition(flicker) {
+open class ClosePipBySwipingDownTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions {
val pipRegion = wmHelper.getWindowRegion(pipApp).bounds
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
index 02f60100d069..7a668897fbbe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
@@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,20 +28,20 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ClosePipBySwipingDownTestCfArm(flicker: FlickerTest) : ClosePipBySwipingDownTest(flicker) {
+class ClosePipBySwipingDownTestCfArm(flicker: LegacyFlickerTest) :
+ ClosePipBySwipingDownTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
index e52b71e602f9..a17144b7cef3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
@@ -20,14 +20,14 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher.Companion.LAUNCHER
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.Test
import org.junit.runners.Parameterized
/** Base class for exiting pip (closing pip window) without returning to the app */
-abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker) {
+abstract class ClosePipTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
setup { this.setRotation(flicker.scenario.startRotation) }
teardown { this.setRotation(Rotation.ROTATION_0) }
@@ -74,15 +74,14 @@ abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker)
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index 86fe583c94e6..dc48696f3197 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -53,7 +53,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ClosePipWithDismissButtonTest(flicker: FlickerTest) : ClosePipTransition(flicker) {
+open class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.closePipWindow(wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
index 05262feceba5..718b14babc4f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
@@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,21 +28,20 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ClosePipWithDismissButtonTestCfArm(flicker: FlickerTest) :
+open class ClosePipWithDismissButtonTestCfArm(flicker: LegacyFlickerTest) :
ClosePipWithDismissButtonTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index 01d67cc35a14..5e392628aa6a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -44,10 +44,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipOnUserLeaveHintTest(flicker: FlickerTest) : EnterPipTransition(flicker) {
- override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions { tapl.goHome() }
- }
+open class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
+ override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
index 90f99c0c4cae..2b3e76a964c4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.wm.shell.flicker.pip
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -27,4 +27,5 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterPipOnUserLeaveHintTestCfArm(flicker: FlickerTest) : EnterPipOnUserLeaveHintTest(flicker)
+class EnterPipOnUserLeaveHintTestCfArm(flicker: LegacyFlickerTest) :
+ EnterPipOnUserLeaveHintTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index 5480144ba1ce..ec35837bc8dd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -21,11 +21,12 @@ import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.entireScreenCovered
@@ -68,15 +69,13 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flicker) {
+open class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val testApp = FixedOrientationAppHelper(instrumentation)
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
override val thisTransition: FlickerBuilder.() -> Unit = {
- teardown {
- testApp.exit(wmHelper)
- }
+ teardown { testApp.exit(wmHelper) }
transitions {
// Enter PiP, and assert that the PiP is within bounds now that the device is back
// in portrait
@@ -95,14 +94,13 @@ open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flic
setup {
// Launch a portrait only app on the fullscreen stack
testApp.launchViaIntent(
- wmHelper,
- stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())
+ wmHelper,
+ stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())
)
// Launch the PiP activity fixed as landscape, but don't enter PiP
pipApp.launchViaIntent(
- wmHelper,
- stringExtras =
- mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
+ wmHelper,
+ stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
)
}
}
@@ -207,13 +205,13 @@ open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flic
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ return LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
index 58416660826f..92642197e9be 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
@@ -17,9 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -29,19 +30,19 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipToOtherOrientationCfArm(flicker: FlickerTest) :
+open class EnterPipToOtherOrientationCfArm(flicker: LegacyFlickerTest) :
EnterPipToOtherOrientation(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ return LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
index cdbdb85a9195..6d20740e239c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
@@ -20,16 +20,14 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.Test
import org.junit.runners.Parameterized
-abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker) {
+abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
- setup {
- pipApp.launchViaIntent(wmHelper)
- }
+ setup { pipApp.launchViaIntent(wmHelper) }
}
/** Checks [pipApp] window remains visible throughout the animation */
@@ -126,15 +124,14 @@ abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker)
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index 95725b64a48a..76c811cbbeea 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -18,7 +18,7 @@ package com.android.wm.shell.flicker.pip
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -50,7 +50,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipViaAppUiButtonTest(flicker: FlickerTest) : EnterPipTransition(flicker) {
+open class EnterPipViaAppUiButtonTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.clickEnterPipButton(wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
index 4390f0bb70b2..78e80497747c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
@@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,20 +28,20 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterPipViaAppUiButtonTestCfArm(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) {
+class EnterPipViaAppUiButtonTestCfArm(flicker: LegacyFlickerTest) :
+ EnterPipViaAppUiButtonTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index 5ac9829b6c8f..dfffba831dc3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -19,14 +19,14 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.Test
import org.junit.runners.Parameterized
/** Base class for pip expand tests */
-abstract class ExitPipToAppTransition(flicker: FlickerTest) : PipTransition(flicker) {
+abstract class ExitPipToAppTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) {
protected val testApp = SimpleAppHelper(instrumentation)
/**
@@ -130,15 +130,14 @@ abstract class ExitPipToAppTransition(flicker: FlickerTest) : PipTransition(flic
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index 0b3d16a8087d..b80b7483ba4d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -18,7 +18,7 @@ package com.android.wm.shell.flicker.pip
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -52,7 +52,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExitPipToAppViaExpandButtonTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) {
+open class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) :
+ ExitPipToAppTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
setup {
// launch an app behind the pip one
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
index eccb85d98798..e25c0d6eddc0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
@@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,21 +28,20 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExitPipToAppViaExpandButtonTestCfArm(flicker: FlickerTest) :
+class ExitPipToAppViaExpandButtonTestCfArm(flicker: LegacyFlickerTest) :
ExitPipToAppViaExpandButtonTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index bb2d40becdc9..f003ed8a77e0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -18,7 +18,7 @@ package com.android.wm.shell.flicker.pip
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -51,7 +51,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExitPipToAppViaIntentTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) {
+open class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
setup {
// launch an app behind the pip one
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
index 6ab6a1f0bb73..be19f3cd1970 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
@@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,20 +28,20 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExitPipToAppViaIntentTestCfArm(flicker: FlickerTest) : ExitPipToAppViaIntentTest(flicker) {
+class ExitPipToAppViaIntentTestCfArm(flicker: LegacyFlickerTest) :
+ ExitPipToAppViaIntentTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index fd16b6ea6ada..a1d3a117482e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -21,8 +21,8 @@ import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -55,7 +55,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExpandPipOnDoubleClickTest(flicker: FlickerTest) : PipTransition(flicker) {
+open class ExpandPipOnDoubleClickTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.doubleClickPipWindow(wmHelper) }
}
@@ -142,15 +142,14 @@ open class ExpandPipOnDoubleClickTest(flicker: FlickerTest) : PipTransition(flic
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
index c09623490041..3095cac94598 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
@@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,21 +28,20 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExpandPipOnDoubleClickTestTestCfArm(flicker: FlickerTest) :
+class ExpandPipOnDoubleClickTestTestCfArm(flicker: LegacyFlickerTest) :
ExpandPipOnDoubleClickTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
index 253aa4cae5c7..8c8d280aea9a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
@@ -20,8 +20,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -34,7 +34,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExpandPipOnPinchOpenTest(flicker: FlickerTest) : PipTransition(flicker) {
+open class ExpandPipOnPinchOpenTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) }
}
@@ -55,15 +55,14 @@ open class ExpandPipOnPinchOpenTest(flicker: FlickerTest) : PipTransition(flicke
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
index e064bf2ee921..1a1ce6823f3b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
@@ -18,8 +18,8 @@ package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,20 +28,20 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExpandPipOnPinchOpenTestCfArm(flicker: FlickerTest) : ExpandPipOnPinchOpenTest(flicker) {
+class ExpandPipOnPinchOpenTestCfArm(flicker: LegacyFlickerTest) :
+ ExpandPipOnPinchOpenTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
index 094060f86691..4f88184e3c20 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.Direction
import org.junit.FixMethodOrder
@@ -55,7 +55,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class MovePipDownOnShelfHeightChange(flicker: FlickerTest) : MovePipShelfHeightTransition(flicker) {
+class MovePipDownOnShelfHeightChange(flicker: LegacyFlickerTest) :
+ MovePipShelfHeightTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
teardown { testApp.exit(wmHelper) }
transitions { testApp.launchViaIntent(wmHelper) }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
index ff51c27bf116..dffc822e7aec 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
@@ -18,11 +18,12 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -38,7 +39,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class MovePipOnImeVisibilityChangeTest(flicker: FlickerTest) : PipTransition(flicker) {
+open class MovePipOnImeVisibilityChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val imeApp = ImeAppHelper(instrumentation)
override val thisTransition: FlickerBuilder.() -> Unit = {
@@ -80,7 +81,7 @@ open class MovePipOnImeVisibilityChangeTest(flicker: FlickerTest) : PipTransitio
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ return LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
index d3d77d20662e..63292a4f2ca3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
@@ -17,9 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,7 +29,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class MovePipOnImeVisibilityChangeTestCfArm(flicker: FlickerTest) :
+class MovePipOnImeVisibilityChangeTestCfArm(flicker: LegacyFlickerTest) :
MovePipOnImeVisibilityChangeTest(flicker) {
companion object {
private const val TAG_IME_VISIBLE = "imeIsVisible"
@@ -36,7 +37,7 @@ class MovePipOnImeVisibilityChangeTestCfArm(flicker: FlickerTest) :
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ return LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
index 109354ab5c79..9a2fa095c8cb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
@@ -19,15 +19,15 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.common.flicker.subject.region.RegionSubject
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
import com.android.wm.shell.flicker.Direction
import org.junit.Test
import org.junit.runners.Parameterized
/** Base class for pip tests with Launcher shelf height change */
-abstract class MovePipShelfHeightTransition(flicker: FlickerTest) : PipTransition(flicker) {
+abstract class MovePipShelfHeightTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) {
protected val testApp = FixedOrientationAppHelper(instrumentation)
/** Checks [pipApp] window remains visible throughout the animation */
@@ -111,15 +111,14 @@ abstract class MovePipShelfHeightTransition(flicker: FlickerTest) : PipTransitio
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
index 27b061b67a85..afb4af6c5b21 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.Direction
import org.junit.FixMethodOrder
@@ -55,14 +55,13 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class MovePipUpOnShelfHeightChangeTest(flicker: FlickerTest) :
+open class MovePipUpOnShelfHeightChangeTest(flicker: LegacyFlickerTest) :
MovePipShelfHeightTransition(flicker) {
- override val thisTransition: FlickerBuilder.() -> Unit =
- {
- setup { testApp.launchViaIntent(wmHelper) }
- transitions { tapl.pressHome() }
- teardown { testApp.exit(wmHelper) }
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ setup { testApp.launchViaIntent(wmHelper) }
+ transitions { tapl.pressHome() }
+ teardown { testApp.exit(wmHelper) }
+ }
/** Checks that the visible region of [pipApp] window always moves up during the animation. */
@Presubmit @Test fun pipWindowMovesUp() = pipWindowMoves(Direction.UP)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
index 9f81ba8eee87..7085d559f3f2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -20,8 +20,8 @@ import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.RequiresDevice
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
import org.junit.FixMethodOrder
import org.junit.Test
@@ -34,7 +34,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipDragTest(flicker: FlickerTest) : PipTransition(flicker) {
+class PipDragTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private var isDraggedLeft: Boolean = true
override val thisTransition: FlickerBuilder.() -> Unit = {
@@ -81,13 +81,11 @@ class PipDragTest(flicker: FlickerTest) : PipTransition(flicker) {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index 9fe9f52fd4af..2b87766daab3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Postsubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
@@ -38,7 +38,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker) {
+class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
// represents the direction in which the pip window should be snapping
private var willSnapRight: Boolean = true
@@ -99,15 +99,14 @@ class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker) {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index 60bf5ffdc7af..adc5ee32cdd3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Postsubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -36,7 +36,7 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 270677470)
-class PipPinchInTest(flicker: FlickerTest) : PipTransition(flicker) {
+class PipPinchInTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) }
}
@@ -57,15 +57,14 @@ class PipPinchInTest(flicker: FlickerTest) : PipTransition(flicker) {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index 17a178f78de3..096af39488e9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -22,7 +22,7 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import android.tools.device.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.PipAppHelper
@@ -32,7 +32,7 @@ import com.android.wm.shell.flicker.BaseTest
import com.google.common.truth.Truth
import org.junit.Test
-abstract class PipTransition(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected val pipApp = PipAppHelper(instrumentation)
protected val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
@@ -78,16 +78,16 @@ abstract class PipTransition(flicker: FlickerTest) : BaseTest(flicker) {
/** Defines the default method of entering PiP */
protected open val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
- pipApp.launchViaIntentAndWaitForPip(wmHelper,
- stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true"))
+ pipApp.launchViaIntentAndWaitForPip(
+ wmHelper,
+ stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")
+ )
}
}
/** Defines the default teardown required to clean up after the test */
protected open val defaultTeardown: FlickerBuilder.() -> Unit = {
- teardown {
- pipApp.exit(wmHelper)
- }
+ teardown { pipApp.exit(wmHelper) }
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index c618e5a24fdf..c315e744bd55 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -21,10 +21,11 @@ import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -46,7 +47,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransition(flicker) {
+open class SetRequestedOrientationWhilePinned(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
@@ -69,20 +70,19 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit
setup {
// Launch the PiP activity fixed as landscape.
pipApp.launchViaIntent(
- wmHelper,
- stringExtras =
- mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
+ wmHelper,
+ stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
)
// Enter PiP.
broadcastActionTrigger.doAction(ActivityOptions.Pip.ACTION_ENTER_PIP)
// System bar may fade out during fixed rotation.
wmHelper
- .StateSyncBuilder()
- .withPipShown()
- .withRotation(Rotation.ROTATION_0)
- .withNavOrTaskBarVisible()
- .withStatusBarVisible()
- .waitForAndVerify()
+ .StateSyncBuilder()
+ .withPipShown()
+ .withRotation(Rotation.ROTATION_0)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
}
}
@@ -150,7 +150,7 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ return LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
index 43d6c8f26126..0ff9cfff873e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
@@ -17,10 +17,11 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -58,7 +59,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowPipAndRotateDisplay(flicker: FlickerTest) : PipTransition(flicker) {
+open class ShowPipAndRotateDisplay(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val testApp = SimpleAppHelper(instrumentation)
private val screenBoundsStart = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
private val screenBoundsEnd = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
@@ -154,13 +155,13 @@ open class ShowPipAndRotateDisplay(flicker: FlickerTest) : PipTransition(flicker
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.rotationTests()
+ return LegacyFlickerTestFactory.rotationTests()
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
index b7a2c47e3b32..25164711b2e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
@@ -16,9 +16,10 @@
package com.android.wm.shell.flicker.pip
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -27,18 +28,18 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowPipAndRotateDisplayCfArm(flicker: FlickerTest) : ShowPipAndRotateDisplay(flicker) {
+class ShowPipAndRotateDisplayCfArm(flicker: LegacyFlickerTest) : ShowPipAndRotateDisplay(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.rotationTests()
+ return LegacyFlickerTestFactory.rotationTests()
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index d1f0980786bf..a43ad9b4dd39 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -23,8 +23,8 @@ import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.EdgeExtensionComponentMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -45,13 +45,13 @@ import org.junit.runners.Parameterized
/**
* Test copy content from the left to the right side of the split-screen.
*
- * To run this test: `atest WMShellFlickerTests:CopyContentInSplit`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:CopyContentInSplit`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CopyContentInSplit(override val flicker: FlickerTest) :
+class CopyContentInSplit(override val flicker: LegacyFlickerTest) :
CopyContentInSplitBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -136,8 +136,6 @@ class CopyContentInSplit(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index 4505b9978b76..0b8f109b0e42 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -21,7 +21,7 @@ import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
@@ -41,13 +41,13 @@ import org.junit.runners.Parameterized
/**
* Test dismiss split screen by dragging the divider bar.
*
- * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByDivider`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:DismissSplitScreenByDivider`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class DismissSplitScreenByDivider(override val flicker: FlickerTest) :
+class DismissSplitScreenByDivider(override val flicker: LegacyFlickerTest) :
DismissSplitScreenByDividerBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index e05b22141e4d..38d4b4029c64 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -20,8 +20,8 @@ import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesInvisible
@@ -38,13 +38,13 @@ import org.junit.runners.Parameterized
/**
* Test dismiss split screen by go home.
*
- * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByGoHome`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:DismissSplitScreenByGoHome`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class DismissSplitScreenByGoHome(override val flicker: FlickerTest) :
+class DismissSplitScreenByGoHome(override val flicker: LegacyFlickerTest) :
DismissSplitScreenByGoHomeBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -154,8 +154,6 @@ class DismissSplitScreenByGoHome(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 63c5d14d85e1..a118c08b35e2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -43,13 +43,13 @@ import org.junit.runners.Parameterized
/**
* Test resize split by dragging the divider bar.
*
- * To run this test: `atest WMShellFlickerTests:DragDividerToResize`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:DragDividerToResize`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class DragDividerToResize(override val flicker: FlickerTest) :
+class DragDividerToResize(override val flicker: LegacyFlickerTest) :
DragDividerToResizeBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -127,8 +127,6 @@ class DragDividerToResize(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index e55868675da7..05c048050b3b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -22,8 +22,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -45,13 +45,13 @@ import org.junit.runners.Parameterized
* Test enter split screen by dragging app icon from all apps. This test is only for large screen
* devices.
*
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromAllApps`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromAllApps`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromAllApps(override val flicker: FlickerTest) :
+class EnterSplitScreenByDragFromAllApps(override val flicker: LegacyFlickerTest) :
EnterSplitScreenByDragFromAllAppsBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -160,11 +160,10 @@ class EnterSplitScreenByDragFromAllApps(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index ab8ecc54e71c..3a75fa60a865 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -22,8 +22,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -44,13 +44,13 @@ import org.junit.runners.Parameterized
* Test enter split screen by dragging app icon from notification. This test is only for large
* screen devices.
*
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromNotification`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromNotification`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromNotification(override val flicker: FlickerTest) :
+class EnterSplitScreenByDragFromNotification(override val flicker: LegacyFlickerTest) :
EnterSplitScreenByDragFromNotificationBenchmark(flicker), ICommonAssertions {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -162,11 +162,10 @@ class EnterSplitScreenByDragFromNotification(override val flicker: FlickerTest)
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
index 516ca97bc531..6d73f92637db 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
@@ -41,13 +41,13 @@ import org.junit.runners.Parameterized
/**
* Test enter split screen by dragging a shortcut. This test is only for large screen devices.
*
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromShortcut`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromShortcut`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromShortcut(override val flicker: FlickerTest) :
+class EnterSplitScreenByDragFromShortcut(override val flicker: LegacyFlickerTest) :
EnterSplitScreenByDragFromShortcutBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
@@ -105,11 +105,10 @@ class EnterSplitScreenByDragFromShortcut(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index 4af7e248b660..15cae6947f88 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -22,8 +22,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -45,13 +45,13 @@ import org.junit.runners.Parameterized
* Test enter split screen by dragging app icon from taskbar. This test is only for large screen
* devices.
*
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromTaskbar`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromTaskbar`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromTaskbar(override val flicker: FlickerTest) :
+class EnterSplitScreenByDragFromTaskbar(override val flicker: LegacyFlickerTest) :
EnterSplitScreenByDragFromTaskbarBenchmark(flicker), ICommonAssertions {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -163,10 +163,9 @@ class EnterSplitScreenByDragFromTaskbar(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index faad9e82ffef..90399fca2574 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -20,8 +20,8 @@ import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -40,13 +40,13 @@ import org.junit.runners.Parameterized
/**
* Test enter split screen from Overview.
*
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromOverview`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenFromOverview`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenFromOverview(override val flicker: FlickerTest) :
+class EnterSplitScreenFromOverview(override val flicker: LegacyFlickerTest) :
EnterSplitScreenFromOverviewBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -106,8 +106,6 @@ class EnterSplitScreenFromOverview(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
index 195b73a14a72..580b153421a4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
@@ -18,11 +18,11 @@ package com.android.wm.shell.flicker.splitscreen
import android.content.Context
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.BaseBenchmarkTest
-abstract class SplitScreenBase(flicker: FlickerTest) : BaseBenchmarkTest(flicker) {
+abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) {
protected val context: Context = instrumentation.context
protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
index 1063dfd8d737..f4828f14d5ed 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
@@ -39,6 +39,7 @@ import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.NotificationAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import org.junit.Assert.assertNotNull
@@ -112,6 +113,17 @@ internal object SplitScreenUtils {
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
+ fun enterSplitViaIntent(
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
+ ) {
+ val stringExtras = mapOf(Primary.EXTRA_LAUNCH_ADJACENT to "true")
+ primaryApp.launchViaIntent(wmHelper, null, null,
+ stringExtras)
+ waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
// Note: The initial split position in landscape is different between tablet and phone.
// In landscape, tablet will let the first app split to right side, and phone will
@@ -314,14 +326,14 @@ internal object SplitScreenUtils {
dividerBar.drag(
Point(
if (dragToRight) {
- displayBounds.width * 4 / 5
+ displayBounds.right
} else {
- displayBounds.width * 1 / 5
+ displayBounds.left
},
if (dragToBottom) {
- displayBounds.height * 4 / 5
+ displayBounds.bottom
} else {
- displayBounds.height * 1 / 5
+ displayBounds.top
}
)
)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index 03b8a75a1f32..e0a47b394ba1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -22,8 +22,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -44,13 +44,13 @@ import org.junit.runners.Parameterized
/**
* Test double tap the divider bar to switch the two apps.
*
- * To run this test: `atest WMShellFlickerTests:SwitchAppByDoubleTapDivider`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchAppByDoubleTapDivider`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchAppByDoubleTapDivider(override val flicker: FlickerTest) :
+class SwitchAppByDoubleTapDivider(override val flicker: LegacyFlickerTest) :
SwitchAppByDoubleTapDividerBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -120,11 +120,10 @@ class SwitchAppByDoubleTapDivider(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 078d95de1dd0..a4060092b422 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -39,13 +39,13 @@ import org.junit.runners.Parameterized
/**
* Test quick switch to split pair from another app.
*
- * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromAnotherApp`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromAnotherApp`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBackToSplitFromAnotherApp(override val flicker: FlickerTest) :
+class SwitchBackToSplitFromAnotherApp(override val flicker: LegacyFlickerTest) :
SwitchBackToSplitFromAnotherAppBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -149,11 +149,10 @@ class SwitchBackToSplitFromAnotherApp(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index 7c84243e00d7..251bd1030da3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -39,13 +39,13 @@ import org.junit.runners.Parameterized
/**
* Test quick switch to split pair from home.
*
- * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromHome`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBackToSplitFromHome(override val flicker: FlickerTest) :
+class SwitchBackToSplitFromHome(override val flicker: LegacyFlickerTest) :
SwitchBackToSplitFromHomeBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -149,11 +149,10 @@ class SwitchBackToSplitFromHome(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index 7c46d3e099a2..1dd45fef30cc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -39,13 +39,13 @@ import org.junit.runners.Parameterized
/**
* Test switch back to split pair from recent.
*
- * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromRecent`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromRecent`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBackToSplitFromRecent(override val flicker: FlickerTest) :
+class SwitchBackToSplitFromRecent(override val flicker: LegacyFlickerTest) :
SwitchBackToSplitFromRecentBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -149,11 +149,10 @@ class SwitchBackToSplitFromRecent(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
index 674ba40f6a1f..8f867df3fea1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -47,13 +47,13 @@ import org.junit.runners.Parameterized
/**
* Test quick switch between two split pairs.
*
- * To run this test: `atest WMShellFlickerTests:SwitchBetweenSplitPairs`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBetweenSplitPairs`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBetweenSplitPairs(override val flicker: FlickerTest) :
+class SwitchBetweenSplitPairs(override val flicker: LegacyFlickerTest) :
SwitchBetweenSplitPairsBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -223,8 +223,6 @@ class SwitchBetweenSplitPairs(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index 676c150815ad..994d6cbfaa1f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -21,8 +21,8 @@ import android.tools.common.NavBar
import android.tools.common.flicker.subject.region.RegionSubject
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -37,21 +37,21 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test unlocking insecure keyguard to back to split screen tasks and verify the transition behavior.
+ * Test unlocking insecure keyguard to back to split screen tasks and verify the transition
+ * behavior.
*
- * To run this test: `atest WMShellFlickerTests:UnlockKeyguardToSplitScreen`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen`
*/
@RequiresDevice
@Postsubmit
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class UnlockKeyguardToSplitScreen(override val flicker: FlickerTest) :
- UnlockKeyguardToSplitScreenBenchmark(flicker), ICommonAssertions {
+class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) :
+ UnlockKeyguardToSplitScreenBenchmark(flicker), ICommonAssertions {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
- defaultSetup(this)
defaultTeardown(this)
thisTransition(this)
}
@@ -65,33 +65,35 @@ class UnlockKeyguardToSplitScreen(override val flicker: FlickerTest) :
@Test
fun primaryAppBoundsIsVisibleAtEnd() =
- flicker.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp,
- landscapePosLeft = false,
- portraitPosTop = false
- )
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp,
+ landscapePosLeft = false,
+ portraitPosTop = false
+ )
@Test
fun secondaryAppBoundsIsVisibleAtEnd() =
- flicker.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp,
- landscapePosLeft = true,
- portraitPosTop = true
- )
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ secondaryApp,
+ landscapePosLeft = true,
+ portraitPosTop = true
+ )
- @Test
- fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
+ @Test fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
- @Test
- fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(secondaryApp)
+ @Test fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(secondaryApp)
@Test
fun notOverlapsForPrimaryAndSecondaryAppLayers() {
flicker.assertLayers {
this.invoke("notOverlapsForPrimaryAndSecondaryLayers") {
- val primaryAppRegions = it.subjects.filter { subject ->
- subject.name.contains(primaryApp.toLayerName()) && subject.isVisible
- }.mapNotNull { primaryApp -> primaryApp.layer.visibleRegion }.toTypedArray()
+ val primaryAppRegions =
+ it.subjects
+ .filter { subject ->
+ subject.name.contains(primaryApp.toLayerName()) && subject.isVisible
+ }
+ .mapNotNull { primaryApp -> primaryApp.layer.visibleRegion }
+ .toTypedArray()
val primaryAppRegionArea = RegionSubject(primaryAppRegions, it.timestamp)
it.visibleRegion(secondaryApp).notOverlaps(primaryAppRegionArea.region)
@@ -102,10 +104,9 @@ class UnlockKeyguardToSplitScreen(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index c3c5f88eaa29..d1ca9eac198d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
@@ -36,7 +36,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CopyContentInSplitBenchmark(override val flicker: FlickerTest) :
+open class CopyContentInSplitBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val textEditApp = SplitScreenUtils.getIme(instrumentation)
protected val magnifierLayer = ComponentNameMatcher("", "magnifier surface bbq wrapper#")
@@ -72,8 +72,6 @@ open class CopyContentInSplitBenchmark(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 37cd18fad521..73acb1f0cc47 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -20,8 +20,8 @@ import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -36,7 +36,8 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class DismissSplitScreenByDividerBenchmark(flicker: FlickerTest) : SplitScreenBase(flicker) {
+open class DismissSplitScreenByDividerBenchmark(override val flicker: LegacyFlickerTest) :
+ SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
@@ -76,8 +77,6 @@ open class DismissSplitScreenByDividerBenchmark(flicker: FlickerTest) : SplitScr
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index 0ec6dc96bcd9..86ffd2af6748 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -20,8 +20,8 @@ import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -36,7 +36,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class DismissSplitScreenByGoHomeBenchmark(override val flicker: FlickerTest) :
+open class DismissSplitScreenByGoHomeBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -63,8 +63,6 @@ open class DismissSplitScreenByGoHomeBenchmark(override val flicker: FlickerTest
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index 190e2e765bcc..dfde3b669813 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -20,8 +20,8 @@ import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
@@ -37,7 +37,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class DragDividerToResizeBenchmark(override val flicker: FlickerTest) :
+open class DragDividerToResizeBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -69,8 +69,6 @@ open class DragDividerToResizeBenchmark(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
index 3a1d1a4415c3..d13e4134a961 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -39,7 +39,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: FlickerTest) :
+open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
@@ -84,11 +84,10 @@ open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: Flic
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
index 2033b7d64416..1d4166922b13 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -39,8 +39,9 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterSplitScreenByDragFromNotificationBenchmark(override val flicker: FlickerTest) :
- SplitScreenBase(flicker) {
+open class EnterSplitScreenByDragFromNotificationBenchmark(
+ override val flicker: LegacyFlickerTest
+) : SplitScreenBase(flicker) {
protected val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -81,11 +82,10 @@ open class EnterSplitScreenByDragFromNotificationBenchmark(override val flicker:
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
index b7a7110afe25..b4bafa79cd48 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -39,7 +39,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterSplitScreenByDragFromShortcutBenchmark(flicker: FlickerTest) :
+open class EnterSplitScreenByDragFromShortcutBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
@Before
fun before() {
@@ -84,11 +84,10 @@ open class EnterSplitScreenByDragFromShortcutBenchmark(flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
index b1ce62f99e6c..da44ecdb9304 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -39,7 +39,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: FlickerTest) :
+open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -84,10 +84,9 @@ open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: Flic
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index 14f07453b7d1..af06d6da2518 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -20,8 +20,8 @@ import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -36,7 +36,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterSplitScreenFromOverviewBenchmark(override val flicker: FlickerTest) :
+open class EnterSplitScreenFromOverviewBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -72,8 +72,6 @@ open class EnterSplitScreenFromOverviewBenchmark(override val flicker: FlickerTe
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index 65fb1358a9b0..23156b5d1628 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -22,8 +22,8 @@ import android.tools.common.NavBar
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
@@ -39,7 +39,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: FlickerTest) :
+open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -145,11 +145,10 @@ open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: FlickerTes
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index b333aba447a2..2d810d3e2631 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -37,7 +37,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: FlickerTest) :
+open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
private val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
@@ -71,11 +71,10 @@ open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: Flicke
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index a27540efdad7..f6df1e42d1b7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -37,7 +37,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SwitchBackToSplitFromHomeBenchmark(override val flicker: FlickerTest) :
+open class SwitchBackToSplitFromHomeBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -69,11 +69,10 @@ open class SwitchBackToSplitFromHomeBenchmark(override val flicker: FlickerTest)
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index 18bf4ff054e0..ba46bdcdad21 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -37,7 +37,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SwitchBackToSplitFromRecentBenchmark(override val flicker: FlickerTest) :
+open class SwitchBackToSplitFromRecentBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -69,11 +69,10 @@ open class SwitchBackToSplitFromRecentBenchmark(override val flicker: FlickerTes
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index c5fe61e26733..0d871e500688 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -20,8 +20,8 @@ import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
@@ -35,7 +35,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SwitchBetweenSplitPairsBenchmark(override val flicker: FlickerTest) :
+open class SwitchBetweenSplitPairsBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thirdApp = SplitScreenUtils.getIme(instrumentation)
protected val fourthApp = SplitScreenUtils.getSendNotification(instrumentation)
@@ -70,8 +70,6 @@ open class SwitchBetweenSplitPairsBenchmark(override val flicker: FlickerTest) :
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
index 5f16e5b4d65e..7952b7125a34 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
@@ -19,8 +19,8 @@ package com.android.wm.shell.flicker.splitscreen.benchmark
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
@@ -33,11 +33,11 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class UnlockKeyguardToSplitScreenBenchmark(override val flicker: FlickerTest) :
- SplitScreenBase(flicker) {
+open class UnlockKeyguardToSplitScreenBenchmark(override val flicker: LegacyFlickerTest) :
+ SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplitViaIntent(wmHelper, primaryApp, secondaryApp) }
transitions {
device.sleep()
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
@@ -58,10 +58,9 @@ open class UnlockKeyguardToSplitScreenBenchmark(override val flicker: FlickerTes
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto
new file mode 100644
index 000000000000..406ada97a07d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.android.wm.shell.flicker"
+ atrace_apps: "com.android.wm.shell.flicker.other"
+ atrace_apps: "com.android.wm.shell.flicker.bubbles"
+ atrace_apps: "com.android.wm.shell.flicker.pip"
+ atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
index 8278c67a9b4f..0dc16f44340f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
@@ -64,18 +64,27 @@ public class BubbleOverflowTest extends ShellTestCase {
}
@Test
- public void test_initialize() {
+ public void test_initialize_forStack() {
assertThat(mOverflow.getExpandedView()).isNull();
- mOverflow.initialize(mBubbleController);
+ mOverflow.initialize(mBubbleController, /* forBubbleBar= */ false);
assertThat(mOverflow.getExpandedView()).isNotNull();
assertThat(mOverflow.getExpandedView().getBubbleKey()).isEqualTo(BubbleOverflow.KEY);
+ assertThat(mOverflow.getBubbleBarExpandedView()).isNull();
+ }
+
+ @Test
+ public void test_initialize_forBubbleBar() {
+ mOverflow.initialize(mBubbleController, /* forBubbleBar= */ true);
+
+ assertThat(mOverflow.getBubbleBarExpandedView()).isNotNull();
+ assertThat(mOverflow.getExpandedView()).isNull();
}
@Test
public void test_cleanUpExpandedState() {
- mOverflow.createExpandedView();
+ mOverflow.initialize(mBubbleController, /* forBubbleBar= */ false);
assertThat(mOverflow.getExpandedView()).isNotNull();
mOverflow.cleanUpExpandedState();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockToken.java
index 09d474d1f97c..a97c19f17412 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockToken.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.desktopmode;
+package com.android.wm.shell;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -25,16 +25,16 @@ import android.window.WindowContainerToken;
/**
* {@link WindowContainerToken} wrapper that supports a mock binder
*/
-class MockToken {
+public class MockToken {
private final WindowContainerToken mToken;
- MockToken() {
+ public MockToken() {
mToken = mock(WindowContainerToken.class);
IBinder binder = mock(IBinder.class);
when(mToken.asBinder()).thenReturn(binder);
}
- WindowContainerToken token() {
+ public WindowContainerToken token() {
return mToken;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataRepositoryTest.kt
new file mode 100644
index 000000000000..6d9d62d3ad92
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataRepositoryTest.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles
+
+import android.app.ActivityTaskManager
+import android.content.pm.LauncherApps
+import android.content.pm.ShortcutInfo
+import android.util.SparseArray
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.bubbles.storage.BubbleEntity
+import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
+import com.android.wm.shell.common.ShellExecutor
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+
+class BubbleDataRepositoryTest : ShellTestCase() {
+
+ private val user0BubbleEntities = listOf(
+ BubbleEntity(
+ userId = 0,
+ packageName = "com.example.messenger",
+ shortcutId = "shortcut-1",
+ key = "0k1",
+ desiredHeight = 120,
+ desiredHeightResId = 0,
+ title = null,
+ taskId = 1,
+ locus = null,
+ isDismissable = true
+ ),
+ BubbleEntity(
+ userId = 10,
+ packageName = "com.example.chat",
+ shortcutId = "alice and bob",
+ key = "0k2",
+ desiredHeight = 0,
+ desiredHeightResId = 16537428,
+ title = "title",
+ taskId = 2,
+ locus = null
+ ),
+ BubbleEntity(
+ userId = 0,
+ packageName = "com.example.messenger",
+ shortcutId = "shortcut-2",
+ key = "0k3",
+ desiredHeight = 120,
+ desiredHeightResId = 0,
+ title = null,
+ taskId = ActivityTaskManager.INVALID_TASK_ID,
+ locus = null
+ )
+ )
+
+ private val user1BubbleEntities = listOf(
+ BubbleEntity(
+ userId = 1,
+ packageName = "com.example.messenger",
+ shortcutId = "shortcut-1",
+ key = "1k1",
+ desiredHeight = 120,
+ desiredHeightResId = 0,
+ title = null,
+ taskId = 3,
+ locus = null,
+ isDismissable = true
+ ),
+ BubbleEntity(
+ userId = 12,
+ packageName = "com.example.chat",
+ shortcutId = "alice and bob",
+ key = "1k2",
+ desiredHeight = 0,
+ desiredHeightResId = 16537428,
+ title = "title",
+ taskId = 4,
+ locus = null
+ ),
+ BubbleEntity(
+ userId = 1,
+ packageName = "com.example.messenger",
+ shortcutId = "shortcut-2",
+ key = "1k3",
+ desiredHeight = 120,
+ desiredHeightResId = 0,
+ title = null,
+ taskId = ActivityTaskManager.INVALID_TASK_ID,
+ locus = null
+ ),
+ BubbleEntity(
+ userId = 12,
+ packageName = "com.example.chat",
+ shortcutId = "alice",
+ key = "1k4",
+ desiredHeight = 0,
+ desiredHeightResId = 16537428,
+ title = "title",
+ taskId = 5,
+ locus = null
+ )
+ )
+
+ private val mainExecutor = mock(ShellExecutor::class.java)
+ private val launcherApps = mock(LauncherApps::class.java)
+
+ private val persistedBubbles = SparseArray<List<BubbleEntity>>()
+
+ private lateinit var dataRepository: BubbleDataRepository
+ private lateinit var persistentRepository: BubblePersistentRepository
+
+ @Before
+ fun setup() {
+ persistentRepository = spy(BubblePersistentRepository(mContext))
+ dataRepository = BubbleDataRepository(launcherApps, mainExecutor, persistentRepository)
+
+ // Add the bubbles to the persistent repository
+ persistedBubbles.put(0, user0BubbleEntities)
+ persistedBubbles.put(1, user1BubbleEntities)
+ persistentRepository.persistsToDisk(persistedBubbles)
+ }
+
+ @After
+ fun teardown() {
+ // Clean up any persisted bubbles for the next run
+ persistentRepository.persistsToDisk(SparseArray())
+ }
+
+ @Test
+ fun testLoadBubbles_invalidParent() {
+ val activeUserIds = listOf(10, 1, 12) // Missing user 0 in persistedBubbles
+ dataRepository.loadBubbles(1, activeUserIds) {
+ // Verify that user 0 has been removed from the persisted list
+ val entitiesByUser = persistentRepository.readFromDisk()
+ assertThat(entitiesByUser.get(0)).isNull()
+ }
+ }
+
+ @Test
+ fun testLoadBubbles_invalidChild() {
+ val activeUserIds = listOf(0, 10, 1) // Missing user 1's child user 12
+ dataRepository.loadBubbles(1, activeUserIds) {
+ // Build a list to compare against
+ val user1BubblesWithoutUser12 = mutableListOf<Bubble>()
+ val user1EntitiesWithoutUser12 = mutableListOf<BubbleEntity>()
+ for (entity in user1BubbleEntities) {
+ if (entity.userId != 12) {
+ user1BubblesWithoutUser12.add(entity.toBubble())
+ user1EntitiesWithoutUser12.add(entity)
+ }
+ }
+
+ // Verify that user 12 has been removed from the persisted list
+ val entitiesByUser = persistentRepository.readFromDisk()
+ assertThat(entitiesByUser.get(1)).isEqualTo(user1EntitiesWithoutUser12)
+ }
+ }
+
+ private fun BubbleEntity.toBubble(): Bubble {
+ return Bubble(
+ key,
+ mock(ShortcutInfo::class.java),
+ desiredHeight,
+ desiredHeightResId,
+ title,
+ taskId,
+ locus,
+ isDismissable,
+ mainExecutor,
+ mock(Bubbles.BubbleMetadataFlagListener::class.java)
+ )
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
new file mode 100644
index 000000000000..d38b848fbb4d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.bubbles.bar;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.drawable.ColorDrawable;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.core.content.ContextCompat;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class BubbleBarHandleViewTest extends ShellTestCase {
+ private BubbleBarHandleView mHandleView;
+
+ @Before
+ public void setup() {
+ mHandleView = new BubbleBarHandleView(mContext);
+ }
+
+ @Test
+ public void testUpdateHandleColor_lightBg() {
+ mHandleView.updateHandleColor(false /* isRegionDark */, false /* animated */);
+
+ assertTrue(mHandleView.getClipToOutline());
+ assertTrue(mHandleView.getBackground() instanceof ColorDrawable);
+ ColorDrawable bgDrawable = (ColorDrawable) mHandleView.getBackground();
+ assertEquals(bgDrawable.getColor(),
+ ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_dark));
+ }
+
+ @Test
+ public void testUpdateHandleColor_darkBg() {
+ mHandleView.updateHandleColor(true /* isRegionDark */, false /* animated */);
+
+ assertTrue(mHandleView.getClipToOutline());
+ assertTrue(mHandleView.getBackground() instanceof ColorDrawable);
+ ColorDrawable bgDrawable = (ColorDrawable) mHandleView.getBackground();
+ assertEquals(bgDrawable.getColor(),
+ ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_light));
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/LaunchAdjacentControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/LaunchAdjacentControllerTest.kt
new file mode 100644
index 000000000000..9dc816b65d2e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/LaunchAdjacentControllerTest.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common
+
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.MockToken
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class LaunchAdjacentControllerTest : ShellTestCase() {
+
+ private lateinit var controller: LaunchAdjacentController
+
+ @Mock private lateinit var syncQueue: SyncTransactionQueue
+
+ @Before
+ fun setUp() {
+ controller = LaunchAdjacentController(syncQueue)
+ }
+
+ @Test
+ fun newInstance_enabledByDefault() {
+ assertThat(controller.launchAdjacentEnabled).isTrue()
+ }
+
+ @Test
+ fun setLaunchAdjacentRoot_launchAdjacentEnabled_setsFlagRoot() {
+ val token = MockToken().token()
+ controller.setLaunchAdjacentRoot(token)
+ val wct = getLatestTransactionOrFail()
+ assertThat(wct.getSetLaunchAdjacentFlagRootContainer()).isEqualTo(token.asBinder())
+ }
+
+ @Test
+ fun setLaunchAdjacentRoot_launchAdjacentDisabled_doesNotUpdateFlagRoot() {
+ val token = MockToken().token()
+ controller.launchAdjacentEnabled = false
+ controller.setLaunchAdjacentRoot(token)
+ verify(syncQueue, never()).queue(any())
+ }
+
+ @Test
+ fun clearLaunchAdjacentRoot_launchAdjacentEnabled_clearsFlagRoot() {
+ val token = MockToken().token()
+ controller.setLaunchAdjacentRoot(token)
+ controller.clearLaunchAdjacentRoot()
+ val wct = getLatestTransactionOrFail()
+ assertThat(wct.getClearLaunchAdjacentFlagRootContainer()).isEqualTo(token.asBinder())
+ }
+
+ @Test
+ fun clearLaunchAdjacentRoot_launchAdjacentDisabled_clearsFlagRoot() {
+ val token = MockToken().token()
+ controller.setLaunchAdjacentRoot(token)
+ controller.launchAdjacentEnabled = false
+ clearInvocations(syncQueue)
+
+ controller.clearLaunchAdjacentRoot()
+ val wct = getLatestTransactionOrFail()
+ assertThat(wct.getClearLaunchAdjacentFlagRootContainer()).isEqualTo(token.asBinder())
+ }
+
+ @Test
+ fun setLaunchAdjacentEnabled_wasDisabledWithContainerSet_setsFlagRoot() {
+ val token = MockToken().token()
+ controller.setLaunchAdjacentRoot(token)
+ controller.launchAdjacentEnabled = false
+ clearInvocations(syncQueue)
+
+ controller.launchAdjacentEnabled = true
+ val wct = getLatestTransactionOrFail()
+ assertThat(wct.getSetLaunchAdjacentFlagRootContainer()).isEqualTo(token.asBinder())
+ }
+
+ @Test
+ fun setLaunchAdjacentEnabled_containerNotSet_doesNotUpdateFlagRoot() {
+ controller.launchAdjacentEnabled = false
+ controller.launchAdjacentEnabled = true
+ verify(syncQueue, never()).queue(any())
+ }
+
+ @Test
+ fun setLaunchAdjacentEnabled_multipleTimes_setsFlagRootOnce() {
+ val token = MockToken().token()
+ controller.setLaunchAdjacentRoot(token)
+ controller.launchAdjacentEnabled = true
+ controller.launchAdjacentEnabled = true
+ // Only execute once
+ verify(syncQueue).queue(any())
+ }
+
+ @Test
+ fun setLaunchAdjacentDisabled_containerSet_clearsFlagRoot() {
+ val token = MockToken().token()
+ controller.setLaunchAdjacentRoot(token)
+ controller.launchAdjacentEnabled = false
+ val wct = getLatestTransactionOrFail()
+ assertThat(wct.getClearLaunchAdjacentFlagRootContainer()).isEqualTo(token.asBinder())
+ }
+
+ @Test
+ fun setLaunchAdjacentDisabled_containerNotSet_doesNotUpdateFlagRoot() {
+ controller.launchAdjacentEnabled = false
+ verify(syncQueue, never()).queue(any())
+ }
+
+ @Test
+ fun setLaunchAdjacentDisabled_multipleTimes_setsFlagRootOnce() {
+ val token = MockToken().token()
+ controller.setLaunchAdjacentRoot(token)
+ clearInvocations(syncQueue)
+ controller.launchAdjacentEnabled = false
+ controller.launchAdjacentEnabled = false
+ // Only execute once
+ verify(syncQueue).queue(any())
+ }
+
+ private fun getLatestTransactionOrFail(): WindowContainerTransaction {
+ val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ verify(syncQueue, atLeastOnce()).queue(arg.capture())
+ return arg.allValues.last().also { assertThat(it).isNotNull() }
+ }
+}
+
+private fun WindowContainerTransaction.getSetLaunchAdjacentFlagRootContainer(): IBinder {
+ return hierarchyOps
+ // Find the operation with the correct type
+ .filter { op -> op.type == HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT }
+ // For set flag root operation, toTop is false
+ .filter { op -> !op.toTop }
+ .map { it.container }
+ .first()
+}
+
+private fun WindowContainerTransaction.getClearLaunchAdjacentFlagRootContainer(): IBinder {
+ return hierarchyOps
+ // Find the operation with the correct type
+ .filter { op -> op.type == HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT }
+ // For clear flag root operation, toTop is true
+ .filter { op -> op.toTop }
+ .map { it.container }
+ .first()
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
new file mode 100644
index 000000000000..145c8f0ab8af
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common.split;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CURSOR_HOVER_STATES_ENABLED;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.view.InputDevice;
+import android.view.InsetsState;
+import android.view.MotionEvent;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link DividerView} */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DividerViewTest extends ShellTestCase {
+ private @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks;
+ private @Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler;
+ private @Mock DisplayController mDisplayController;
+ private @Mock DisplayImeController mDisplayImeController;
+ private @Mock ShellTaskOrganizer mTaskOrganizer;
+ private SplitLayout mSplitLayout;
+ private DividerView mDividerView;
+
+ @Before
+ @UiThreadTest
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ Configuration configuration = getConfiguration();
+ mSplitLayout = new SplitLayout("TestSplitLayout", mContext, configuration,
+ mSplitLayoutHandler, mCallbacks, mDisplayController, mDisplayImeController,
+ mTaskOrganizer, SplitLayout.PARALLAX_NONE);
+ SplitWindowManager splitWindowManager = new SplitWindowManager("TestSplitWindowManager",
+ mContext,
+ configuration, mCallbacks);
+ splitWindowManager.init(mSplitLayout, new InsetsState());
+ mDividerView = spy((DividerView) splitWindowManager.getDividerView());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testHoverDividerView() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CURSOR_HOVER_STATES_ENABLED,
+ "true", false);
+
+ Rect dividerBounds = mSplitLayout.getDividerBounds();
+ int x = dividerBounds.centerX();
+ int y = dividerBounds.centerY();
+ long downTime = SystemClock.uptimeMillis();
+ mDividerView.onHoverEvent(getMotionEvent(downTime, MotionEvent.ACTION_HOVER_ENTER, x, y));
+
+ verify(mDividerView, times(1)).setHovering();
+
+ mDividerView.onHoverEvent(getMotionEvent(downTime, MotionEvent.ACTION_HOVER_EXIT, x, y));
+
+ verify(mDividerView, times(1)).releaseHovering();
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CURSOR_HOVER_STATES_ENABLED,
+ "false", false);
+ }
+
+ private static MotionEvent getMotionEvent(long eventTime, int action, float x, float y) {
+ MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties();
+ properties.id = 0;
+ properties.toolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+
+ MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+ coords.pressure = 1;
+ coords.size = 1;
+ coords.x = x;
+ coords.y = y;
+
+ return MotionEvent.obtain(eventTime, eventTime, action, 1,
+ new MotionEvent.PointerProperties[]{properties},
+ new MotionEvent.PointerCoords[]{coords}, 0, 0, 1.0f, 1.0f, 0, 0,
+ InputDevice.SOURCE_TOUCHSCREEN, 0);
+ }
+
+ private static Configuration getConfiguration() {
+ final Configuration configuration = new Configuration();
+ configuration.unset();
+ configuration.orientation = ORIENTATION_LANDSCAPE;
+ configuration.windowConfiguration.setRotation(0);
+ configuration.windowConfiguration.setBounds(new Rect(0, 0, 1080, 2160));
+ return configuration;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
index d6387ee5ae13..605a762a395f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
@@ -59,6 +59,7 @@ import android.window.WindowContainerTransaction.HierarchyOp;
import androidx.test.filters.SmallTest;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.wm.shell.MockToken;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index 3bc2f0e8674e..3fe78efdf2b1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -129,6 +129,18 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
}
@Test
+ fun addListener_notifiesStashed() {
+ repo.setStashed(DEFAULT_DISPLAY, true)
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+ executor.flushAll()
+
+ assertThat(listener.stashedOnDefaultDisplay).isTrue()
+ assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1)
+ }
+
+ @Test
fun addListener_tasksOnDifferentDisplay_doesNotNotify() {
repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true)
val listener = TestVisibilityListener()
@@ -313,6 +325,65 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
assertThat(tasks.first()).isEqualTo(6)
}
+ @Test
+ fun setStashed_stateIsUpdatedForTheDisplay() {
+ repo.setStashed(DEFAULT_DISPLAY, true)
+ assertThat(repo.isStashed(DEFAULT_DISPLAY)).isTrue()
+ assertThat(repo.isStashed(SECOND_DISPLAY)).isFalse()
+
+ repo.setStashed(DEFAULT_DISPLAY, false)
+ assertThat(repo.isStashed(DEFAULT_DISPLAY)).isFalse()
+ }
+
+ @Test
+ fun setStashed_notifyListener() {
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+ repo.setStashed(DEFAULT_DISPLAY, true)
+ executor.flushAll()
+ assertThat(listener.stashedOnDefaultDisplay).isTrue()
+ assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1)
+
+ repo.setStashed(DEFAULT_DISPLAY, false)
+ executor.flushAll()
+ assertThat(listener.stashedOnDefaultDisplay).isFalse()
+ assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(2)
+ }
+
+ @Test
+ fun setStashed_secondCallDoesNotNotify() {
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+ repo.setStashed(DEFAULT_DISPLAY, true)
+ repo.setStashed(DEFAULT_DISPLAY, true)
+ executor.flushAll()
+ assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1)
+ }
+
+ @Test
+ fun setStashed_tracksPerDisplay() {
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+
+ repo.setStashed(DEFAULT_DISPLAY, true)
+ executor.flushAll()
+ assertThat(listener.stashedOnDefaultDisplay).isTrue()
+ assertThat(listener.stashedOnSecondaryDisplay).isFalse()
+
+ repo.setStashed(SECOND_DISPLAY, true)
+ executor.flushAll()
+ assertThat(listener.stashedOnDefaultDisplay).isTrue()
+ assertThat(listener.stashedOnSecondaryDisplay).isTrue()
+
+ repo.setStashed(DEFAULT_DISPLAY, false)
+ executor.flushAll()
+ assertThat(listener.stashedOnDefaultDisplay).isFalse()
+ assertThat(listener.stashedOnSecondaryDisplay).isTrue()
+ }
+
class TestListener : DesktopModeTaskRepository.ActiveTasksListener {
var activeChangesOnDefaultDisplay = 0
var activeChangesOnSecondaryDisplay = 0
@@ -332,6 +403,12 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
var visibleChangesOnDefaultDisplay = 0
var visibleChangesOnSecondaryDisplay = 0
+ var stashedOnDefaultDisplay = false
+ var stashedOnSecondaryDisplay = false
+
+ var stashedChangesOnDefaultDisplay = 0
+ var stashedChangesOnSecondaryDisplay = 0
+
override fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {
when (displayId) {
DEFAULT_DISPLAY -> {
@@ -345,6 +422,20 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
else -> fail("Visible task listener received unexpected display id: $displayId")
}
}
+
+ override fun onStashedChanged(displayId: Int, stashed: Boolean) {
+ when (displayId) {
+ DEFAULT_DISPLAY -> {
+ stashedOnDefaultDisplay = stashed
+ stashedChangesOnDefaultDisplay++
+ }
+ SECOND_DISPLAY -> {
+ stashedOnSecondaryDisplay = stashed
+ stashedChangesOnDefaultDisplay++
+ }
+ else -> fail("Visible task listener received unexpected display id: $displayId")
+ }
+ }
}
companion object {
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 1335ebf105a6..1477cf7415cf 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
@@ -39,17 +39,20 @@ 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.wm.shell.MockToken
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.LaunchAdjacentController
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
+import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
@@ -77,6 +80,7 @@ import org.mockito.Mockito.`when` as whenever
class DesktopTasksControllerTest : ShellTestCase() {
@Mock lateinit var testExecutor: ShellExecutor
+ @Mock lateinit var shellCommandHandler: ShellCommandHandler
@Mock lateinit var shellController: ShellController
@Mock lateinit var displayController: DisplayController
@Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
@@ -85,12 +89,16 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Mock lateinit var transitions: Transitions
@Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
@Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
+ @Mock lateinit var mToggleResizeDesktopTaskTransitionHandler:
+ ToggleResizeDesktopTaskTransitionHandler
+ @Mock lateinit var launchAdjacentController: LaunchAdjacentController
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
private lateinit var shellInit: ShellInit
private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository
+ private val shellExecutor = TestShellExecutor()
// Mock running tasks are registered here so we can get the list from mock shell task organizer
private val runningTasks = mutableListOf<RunningTaskInfo>()
@@ -114,6 +122,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
return DesktopTasksController(
context,
shellInit,
+ shellCommandHandler,
shellController,
displayController,
shellTaskOrganizer,
@@ -122,8 +131,10 @@ class DesktopTasksControllerTest : ShellTestCase() {
transitions,
enterDesktopTransitionHandler,
exitDesktopTransitionHandler,
+ mToggleResizeDesktopTaskTransitionHandler,
desktopModeTaskRepository,
- TestShellExecutor()
+ launchAdjacentController,
+ shellExecutor
)
}
@@ -262,8 +273,9 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- fun moveToDesktop() {
+ fun moveToDesktop_displayFullscreen_windowingModeSetToFreeform() {
val task = setUpFullscreenTask()
+ task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
controller.moveToDesktop(task)
val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -271,6 +283,16 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun moveToDesktop_displayFreeform_windowingModeSetToUndefined() {
+ val task = setUpFullscreenTask()
+ task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
+ controller.moveToDesktop(task)
+ val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
fun moveToDesktop_nonExistentTask_doesNothing() {
controller.moveToDesktop(999)
verifyWCTNotExecuted()
@@ -317,12 +339,23 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- fun moveToFullscreen() {
+ fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() {
val task = setUpFreeformTask()
+ task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
controller.moveToFullscreen(task)
val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
+ fun moveToFullscreen_displayFreeform_windowingModeSetToFullscreen() {
+ val task = setUpFreeformTask()
+ task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
+ controller.moveToFullscreen(task)
+ val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
}
@Test
@@ -345,6 +378,18 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun moveTaskToFront_postsWctWithReorderOp() {
+ val task1 = setUpFreeformTask()
+ setUpFreeformTask()
+
+ controller.moveTaskToFront(task1)
+
+ val wct = getLatestWct(expectTransition = TRANSIT_TO_FRONT)
+ assertThat(wct.hierarchyOps).hasSize(1)
+ wct.assertReorderAt(index = 0, task1)
+ }
+
+ @Test
fun moveToNextDisplay_noOtherDisplays() {
whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY))
val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -451,6 +496,27 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun handleRequest_fullscreenTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val stashedFreeformTask = setUpFreeformTask(DEFAULT_DISPLAY)
+ markTaskHidden(stashedFreeformTask)
+
+ val fullscreenTask = createFullscreenTask(DEFAULT_DISPLAY)
+
+ controller.stashDesktopApps(DEFAULT_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+ assertThat(result).isNotNull()
+ result!!.assertReorderSequence(stashedFreeformTask, fullscreenTask)
+ assertThat(result.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+
+ // Stashed state should be cleared
+ assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse()
+ }
+
+ @Test
fun handleRequest_freeformTask_freeformVisible_returnNull() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -501,6 +567,25 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun handleRequest_freeformTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val stashedFreeformTask = setUpFreeformTask(DEFAULT_DISPLAY)
+ markTaskHidden(stashedFreeformTask)
+
+ val freeformTask = createFreeformTask(DEFAULT_DISPLAY)
+
+ controller.stashDesktopApps(DEFAULT_DISPLAY)
+
+ val result = controller.handleRequest(Binder(), createTransition(freeformTask))
+ assertThat(result).isNotNull()
+ result?.assertReorderSequence(stashedFreeformTask, freeformTask)
+
+ // Stashed state should be cleared
+ assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse()
+ }
+
+ @Test
fun handleRequest_notOpenOrToFrontTransition_returnNull() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -539,6 +624,46 @@ class DesktopTasksControllerTest : ShellTestCase() {
assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
}
+ @Test
+ fun stashDesktopApps_stateUpdates() {
+ controller.stashDesktopApps(DEFAULT_DISPLAY)
+
+ assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isTrue()
+ assertThat(desktopModeTaskRepository.isStashed(SECOND_DISPLAY)).isFalse()
+ }
+
+ @Test
+ fun hideStashedDesktopApps_stateUpdates() {
+ desktopModeTaskRepository.setStashed(DEFAULT_DISPLAY, true)
+ desktopModeTaskRepository.setStashed(SECOND_DISPLAY, true)
+ controller.hideStashedDesktopApps(DEFAULT_DISPLAY)
+
+ assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse()
+ // Check that second display is not affected
+ assertThat(desktopModeTaskRepository.isStashed(SECOND_DISPLAY)).isTrue()
+ }
+
+ @Test
+ fun desktopTasksVisibilityChange_visible_setLaunchAdjacentDisabled() {
+ val task = setUpFreeformTask()
+ clearInvocations(launchAdjacentController)
+
+ markTaskVisible(task)
+ shellExecutor.flushAll()
+ verify(launchAdjacentController).launchAdjacentEnabled = false
+ }
+
+ @Test
+ fun desktopTasksVisibilityChange_invisible_setLaunchAdjacentEnabled() {
+ val task = setUpFreeformTask()
+ markTaskVisible(task)
+ clearInvocations(launchAdjacentController)
+
+ markTaskHidden(task)
+ shellExecutor.flushAll()
+ verify(launchAdjacentController).launchAdjacentEnabled = true
+ }
+
private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createFreeformTask(displayId)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
index cf1ff3214d87..29a757c19d98 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -22,6 +22,7 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.view.Display.DEFAULT_DISPLAY
+import com.android.wm.shell.MockToken
import com.android.wm.shell.TestRunningTaskInfoBuilder
class DesktopTestHelpers {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
index 68cb57c14d8c..b1befc46f383 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
@@ -41,6 +41,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/** Tests for {@link MainStage} */
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -61,7 +63,7 @@ public class MainStageTests extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider);
+ mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty());
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index 3b42a48b5a40..549bd3fcabfb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -46,6 +46,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import java.util.Optional;
+
/** Tests for {@link SideStage} */
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -66,7 +68,7 @@ public class SideStageTests extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build();
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider);
+ mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty());
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index fb17d8799bda..e8a1e91acd4d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -61,6 +61,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
@@ -71,6 +72,7 @@ import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.sysui.ShellSharedConstants;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import org.junit.Before;
import org.junit.Test;
@@ -102,6 +104,8 @@ public class SplitScreenControllerTests extends ShellTestCase {
@Mock IconProvider mIconProvider;
@Mock StageCoordinator mStageCoordinator;
@Mock RecentTasksController mRecentTasks;
+ @Mock LaunchAdjacentController mLaunchAdjacentController;
+ @Mock WindowDecorViewModel mWindowDecorViewModel;
@Captor ArgumentCaptor<Intent> mIntentCaptor;
private ShellController mShellController;
@@ -117,7 +121,8 @@ public class SplitScreenControllerTests extends ShellTestCase {
mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
mRootTDAOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mMainExecutor, mStageCoordinator));
+ mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
+ mMainExecutor, mStageCoordinator));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 4e446c684d86..a3009a55198f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -31,12 +31,14 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.util.Optional;
@@ -76,10 +78,13 @@ public class SplitTestUtils {
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks) {
+ Optional<RecentTasksController> recentTasks,
+ LaunchAdjacentController launchAdjacentController,
+ Optional<WindowDecorViewModel> windowDecorViewModel) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
- transitions, transactionPool, mainExecutor, recentTasks);
+ transitions, transactionPool, mainExecutor, recentTasks,
+ launchAdjacentController, windowDecorViewModel);
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 44c76de945b0..5efd9ad97a3e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -70,6 +70,7 @@ import com.android.wm.shell.TransitionInfoBuilder;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
@@ -77,6 +78,7 @@ import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import org.junit.Before;
import org.junit.Test;
@@ -101,7 +103,9 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private Transitions mTransitions;
@Mock private SurfaceSession mSurfaceSession;
@Mock private IconProvider mIconProvider;
+ @Mock private WindowDecorViewModel mWindowDecorViewModel;
@Mock private ShellExecutor mMainExecutor;
+ @Mock private LaunchAdjacentController mLaunchAdjacentController;
@Mock private DefaultMixedHandler mMixedHandler;
private SplitLayout mSplitLayout;
private MainStage mMainStage;
@@ -123,16 +127,17 @@ public class SplitTransitionTests extends ShellTestCase {
mSplitLayout = SplitTestUtils.createMockSplitLayout();
mMainStage = spy(new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
- mIconProvider));
+ mIconProvider, Optional.of(mWindowDecorViewModel)));
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = spy(new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
- mIconProvider));
+ mIconProvider, Optional.of(mWindowDecorViewModel)));
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool, mMainExecutor, Optional.empty());
+ mTransactionPool, mMainExecutor, Optional.empty(),
+ mLaunchAdjacentController, Optional.empty());
mStageCoordinator.setMixedHandler(mMixedHandler);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
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 2dcdc74e8ae7..e59d09cd1ee1 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
@@ -65,6 +65,7 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
@@ -107,6 +108,8 @@ public class StageCoordinatorTests extends ShellTestCase {
private DisplayInsetsController mDisplayInsetsController;
@Mock
private TransactionPool mTransactionPool;
+ @Mock
+ private LaunchAdjacentController mLaunchAdjacentController;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -130,7 +133,7 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
- mMainExecutor, Optional.empty()));
+ mMainExecutor, Optional.empty(), mLaunchAdjacentController, Optional.empty()));
mDividerLeash = new SurfaceControl.Builder(mSurfaceSession).setName("fakeDivider").build();
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 1a1bebd28aef..df1e2e16f485 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -43,6 +43,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import org.junit.Before;
import org.junit.Test;
@@ -52,6 +53,8 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/**
* Tests for {@link StageTaskListener}
* Build/Install/Run:
@@ -71,6 +74,8 @@ public final class StageTaskListenerTests extends ShellTestCase {
private SyncTransactionQueue mSyncQueue;
@Mock
private IconProvider mIconProvider;
+ @Mock
+ private WindowDecorViewModel mWindowDecorViewModel;
@Captor
private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
private SurfaceSession mSurfaceSession = new SurfaceSession();
@@ -89,7 +94,8 @@ public final class StageTaskListenerTests extends ShellTestCase {
mCallbacks,
mSyncQueue,
mSurfaceSession,
- mIconProvider);
+ mIconProvider,
+ Optional.of(mWindowDecorViewModel));
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootTask.parentTaskId = INVALID_TASK_ID;
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
index 41bab95b7dd4..adc2a6fbff23 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
@@ -53,7 +53,6 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopTasksController;
-import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -82,7 +81,6 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private DisplayController mDisplayController;
@Mock private DisplayLayout mDisplayLayout;
- @Mock private SplitScreenController mSplitScreenController;
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private DesktopModeController mDesktopModeController;
@Mock private DesktopTasksController mDesktopTasksController;
@@ -92,6 +90,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
@Mock private DesktopModeWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory;
@Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory;
@Mock private SurfaceControl.Transaction mTransaction;
+ @Mock private Display mDisplay;
private final List<InputManager> mMockInputManagers = new ArrayList<>();
private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel;
@@ -111,7 +110,6 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
mTransitions,
Optional.of(mDesktopModeController),
Optional.of(mDesktopTasksController),
- Optional.of(mSplitScreenController),
mDesktopModeWindowDecorFactory,
mMockInputMonitorFactory,
mTransactionFactory
@@ -129,6 +127,9 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG);
inputChannels[0].dispose();
when(mInputMonitor.getInputChannel()).thenReturn(inputChannels[1]);
+
+ mDesktopModeWindowDecoration.mDisplay = mDisplay;
+ doReturn(Display.DEFAULT_DISPLAY).when(mDisplay).getDisplayId();
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
index 8f84008e8d2d..3fbab0f9e2bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
@@ -55,7 +55,7 @@ class DragDetectorTest {
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(eventHandler.handleMotionEvent(any())).thenReturn(true)
+ `when`(eventHandler.handleMotionEvent(any(), any())).thenReturn(true)
dragDetector = DragDetector(eventHandler)
dragDetector.setTouchSlop(SLOP)
@@ -72,13 +72,13 @@ class DragDetectorTest {
@Test
fun testNoMove_passesDownAndUp() {
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
@@ -86,12 +86,12 @@ class DragDetectorTest {
@Test
fun testMoveInSlop_touch_passesDownAndUp() {
- `when`(eventHandler.handleMotionEvent(argThat {
+ `when`(eventHandler.handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN
})).thenReturn(false)
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
@@ -99,12 +99,12 @@ class DragDetectorTest {
val newX = X + SLOP - 1
assertFalse(
dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
- verify(eventHandler, never()).handleMotionEvent(argThat {
+ verify(eventHandler, never()).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_MOVE
})
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
@@ -112,13 +112,13 @@ class DragDetectorTest {
@Test
fun testMoveInSlop_mouse_passesDownMoveAndUp() {
- `when`(eventHandler.handleMotionEvent(argThat {
+ `when`(eventHandler.handleMotionEvent(any(), argThat {
it.action == MotionEvent.ACTION_DOWN
})).thenReturn(false)
assertFalse(dragDetector.onMotionEvent(
createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
it.source == InputDevice.SOURCE_MOUSE
})
@@ -126,14 +126,14 @@ class DragDetectorTest {
val newX = X + SLOP - 1
assertTrue(dragDetector.onMotionEvent(
createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
it.source == InputDevice.SOURCE_MOUSE
})
assertTrue(dragDetector.onMotionEvent(
createMotionEvent(MotionEvent.ACTION_UP, newX, Y, isTouch = false)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
it.source == InputDevice.SOURCE_MOUSE
})
@@ -141,25 +141,25 @@ class DragDetectorTest {
@Test
fun testMoveBeyondSlop_passesDownMoveAndUp() {
- `when`(eventHandler.handleMotionEvent(argThat {
+ `when`(eventHandler.handleMotionEvent(any(), argThat {
it.action == MotionEvent.ACTION_DOWN
})).thenReturn(false)
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
val newX = X + SLOP + 1
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y &&
it.source == InputDevice.SOURCE_TOUCHSCREEN
})
@@ -167,12 +167,12 @@ class DragDetectorTest {
@Test
fun testPassesHoverEnter() {
- `when`(eventHandler.handleMotionEvent(argThat {
+ `when`(eventHandler.handleMotionEvent(any(), argThat {
it.action == MotionEvent.ACTION_HOVER_ENTER
})).thenReturn(false)
assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_ENTER)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_HOVER_ENTER && it.x == X && it.y == Y
})
}
@@ -180,7 +180,7 @@ class DragDetectorTest {
@Test
fun testPassesHoverMove() {
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_MOVE)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_HOVER_MOVE && it.x == X && it.y == Y
})
}
@@ -188,7 +188,7 @@ class DragDetectorTest {
@Test
fun testPassesHoverExit() {
assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_EXIT)))
- verify(eventHandler).handleMotionEvent(argThat {
+ verify(eventHandler).handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_HOVER_EXIT && it.x == X && it.y == Y
})
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index 69604ddf0af1..6f0599aa8243 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -22,6 +22,7 @@ import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFI
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.argThat
@@ -71,16 +72,6 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
- taskPositioner =
- FluidResizeTaskPositioner(
- mockShellTaskOrganizer,
- mockWindowDecoration,
- mockDisplayController,
- DISALLOWED_AREA_FOR_END_BOUNDS,
- mockDragStartListener,
- mockTransactionFactory
- )
-
whenever(taskToken.asBinder()).thenReturn(taskBinder)
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
@@ -101,6 +92,15 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
}
mockWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
+
+ taskPositioner = FluidResizeTaskPositioner(
+ mockShellTaskOrganizer,
+ mockWindowDecoration,
+ mockDisplayController,
+ mockDragStartListener,
+ mockTransactionFactory,
+ DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT
+ )
}
@Test
@@ -544,7 +544,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
)
val newX = STARTING_BOUNDS.right.toFloat() + 5
- val newY = STARTING_BOUNDS.top.toFloat() + 5
+ val newY = DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT.toFloat() - 1
taskPositioner.onDragPositioningMove(
newX,
newY
@@ -614,6 +614,38 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
})
}
+ @Test
+ fun testDragResize_drag_taskPositionedInStableBounds() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_UNDEFINED, // drag
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ val newX = STARTING_BOUNDS.left.toFloat()
+ val newY = STABLE_BOUNDS.top.toFloat() - 5
+ taskPositioner.onDragPositioningMove(
+ newX,
+ newY
+ )
+ verify(mockTransaction).setPosition(any(), eq(newX), eq(newY))
+
+ taskPositioner.onDragPositioningEnd(
+ newX,
+ newY
+ )
+ // Verify task's top bound is set to stable bounds top since dragged outside stable bounds
+ // but not in disallowed end bounds area.
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds.top ==
+ STABLE_BOUNDS.top
+ }
+ })
+ }
+
companion object {
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
@@ -622,10 +654,11 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
private const val DEFAULT_MIN = 40
private const val DISPLAY_ID = 1
private const val NAVBAR_HEIGHT = 50
+ private const val CAPTION_HEIGHT = 50
+ private const val DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT = 10
private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
- private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
+ private val STARTING_BOUNDS = Rect(100, 100, 200, 200)
private val STABLE_INSETS = Rect(0, 50, 0, 0)
- private val DISALLOWED_AREA_FOR_END_BOUNDS = Rect(0, 0, 300, 300)
private val DISALLOWED_RESIZE_AREA = Rect(
DISPLAY_BOUNDS.left,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
@@ -633,7 +666,7 @@ class FluidResizeTaskPositionerTest : ShellTestCase() {
DISPLAY_BOUNDS.bottom)
private val STABLE_BOUNDS = Rect(
DISPLAY_BOUNDS.left,
- DISPLAY_BOUNDS.top,
+ DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
DISPLAY_BOUNDS.right,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 4147dd8e6152..3465ddd9d101 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -89,17 +89,6 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
- taskPositioner =
- VeiledResizeTaskPositioner(
- mockShellTaskOrganizer,
- mockDesktopWindowDecoration,
- mockDisplayController,
- DISALLOWED_AREA_FOR_END_BOUNDS,
- mockDragStartListener,
- mockTransactionFactory,
- mockTransitions
- )
-
whenever(taskToken.asBinder()).thenReturn(taskBinder)
whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
whenever(mockDisplayLayout.densityDpi()).thenReturn(DENSITY_DPI)
@@ -119,6 +108,17 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
}
mockDesktopWindowDecoration.mDisplay = mockDisplay
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
+
+ taskPositioner =
+ VeiledResizeTaskPositioner(
+ mockShellTaskOrganizer,
+ mockDesktopWindowDecoration,
+ mockDisplayController,
+ mockDragStartListener,
+ mockTransactionFactory,
+ mockTransitions,
+ DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT
+ )
}
@Test
@@ -269,7 +269,7 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
)
val newX = STARTING_BOUNDS.left.toFloat() + 5
- val newY = STARTING_BOUNDS.top.toFloat() + 5
+ val newY = DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT.toFloat() - 1
taskPositioner.onDragPositioningMove(
newX,
newY
@@ -334,6 +334,38 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
})
}
+ @Test
+ fun testDragResize_drag_taskPositionedInStableBounds() {
+ taskPositioner.onDragPositioningStart(
+ CTRL_TYPE_UNDEFINED, // drag
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ val newX = STARTING_BOUNDS.left.toFloat()
+ val newY = STABLE_BOUNDS.top.toFloat() - 5
+ taskPositioner.onDragPositioningMove(
+ newX,
+ newY
+ )
+ verify(mockTransaction).setPosition(any(), eq(newX), eq(newY))
+
+ taskPositioner.onDragPositioningEnd(
+ newX,
+ newY
+ )
+ // Verify task's top bound is set to stable bounds top since dragged outside stable bounds
+ // but not in disallowed end bounds area.
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
+ change.configuration.windowConfiguration.bounds.top ==
+ STABLE_BOUNDS.top
+ }
+ })
+ }
+
companion object {
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
@@ -342,12 +374,13 @@ class VeiledResizeTaskPositionerTest : ShellTestCase() {
private const val DEFAULT_MIN = 40
private const val DISPLAY_ID = 1
private const val NAVBAR_HEIGHT = 50
+ private const val CAPTION_HEIGHT = 50
+ private const val DISALLOWED_AREA_FOR_END_BOUNDS_HEIGHT = 10
private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
- private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
- private val DISALLOWED_AREA_FOR_END_BOUNDS = Rect(0, 0, 50, 50)
+ private val STARTING_BOUNDS = Rect(100, 100, 200, 200)
private val STABLE_BOUNDS = Rect(
DISPLAY_BOUNDS.left,
- DISPLAY_BOUNDS.top,
+ DISPLAY_BOUNDS.top + CAPTION_HEIGHT,
DISPLAY_BOUNDS.right,
DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT
)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 5a2326b9c393..7fc1c99bb44e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -57,7 +57,6 @@ import android.window.SurfaceSyncGroup;
import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
@@ -316,7 +315,8 @@ public class WindowDecorationTests extends ShellTestCase {
releaseOrder.verify(t).remove(captionContainerSurface);
releaseOrder.verify(t).remove(decorContainerSurface);
releaseOrder.verify(t).apply();
- verify(mMockWindowContainerTransaction)
+ // Expect to remove two insets sources, the caption insets and the mandatory gesture insets.
+ verify(mMockWindowContainerTransaction, Mockito.times(2))
.removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt());
}
@@ -410,15 +410,17 @@ public class WindowDecorationTests extends ShellTestCase {
verify(additionalWindowSurfaceBuilder).build();
verify(mMockSurfaceControlAddWindowT).setPosition(additionalWindowSurface, 0, 0);
final int width = WindowDecoration.loadDimensionPixelSize(
- mContext.getResources(), mCaptionMenuWidthId);
+ windowDecor.mDecorWindowContext.getResources(), mCaptionMenuWidthId);
final int height = WindowDecoration.loadDimensionPixelSize(
- mContext.getResources(), mRelayoutParams.mCaptionHeightId);
+ windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId);
verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height);
- final int shadowRadius = WindowDecoration.loadDimensionPixelSize(mContext.getResources(),
+ final int shadowRadius = WindowDecoration.loadDimensionPixelSize(
+ windowDecor.mDecorWindowContext.getResources(),
mCaptionMenuShadowRadiusId);
verify(mMockSurfaceControlAddWindowT)
.setShadowRadius(additionalWindowSurface, shadowRadius);
- final int cornerRadius = WindowDecoration.loadDimensionPixelSize(mContext.getResources(),
+ final int cornerRadius = WindowDecoration.loadDimensionPixelSize(
+ windowDecor.mDecorWindowContext.getResources(),
mCaptionMenuCornerRadiusId);
verify(mMockSurfaceControlAddWindowT)
.setCornerRadius(additionalWindowSurface, cornerRadius);
@@ -513,8 +515,7 @@ public class WindowDecorationTests extends ShellTestCase {
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
- return new TestWindowDecoration(InstrumentationRegistry.getInstrumentation().getContext(),
- mMockDisplayController, mMockShellTaskOrganizer,
+ return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
taskInfo, testSurface,
new MockObjectSupplier<>(mMockSurfaceControlBuilders,
() -> createMockSurfaceControlBuilder(mock(SurfaceControl.class))),
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 00919dc3f22a..f71e7289bd37 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -79,7 +79,7 @@ bool ShaderCache::validateCache(const void* identity, ssize_t size) {
void ShaderCache::initShaderDiskCache(const void* identity, ssize_t size) {
ATRACE_NAME("initShaderDiskCache");
- std::lock_guard<std::mutex> lock(mMutex);
+ std::lock_guard lock(mMutex);
// Emulators can switch between different renders either as part of config
// or snapshot migration. Also, program binaries may not work well on some
@@ -92,7 +92,7 @@ void ShaderCache::initShaderDiskCache(const void* identity, ssize_t size) {
}
void ShaderCache::setFilename(const char* filename) {
- std::lock_guard<std::mutex> lock(mMutex);
+ std::lock_guard lock(mMutex);
mFilename = filename;
}
@@ -104,7 +104,7 @@ BlobCache* ShaderCache::getBlobCacheLocked() {
sk_sp<SkData> ShaderCache::load(const SkData& key) {
ATRACE_NAME("ShaderCache::load");
size_t keySize = key.size();
- std::lock_guard<std::mutex> lock(mMutex);
+ std::lock_guard lock(mMutex);
if (!mInitialized) {
return nullptr;
}
@@ -181,13 +181,18 @@ void ShaderCache::saveToDiskLocked() {
auto key = sIDKey;
set(mBlobCache.get(), &key, sizeof(key), mIDHash.data(), mIDHash.size());
}
+ // The most straightforward way to make ownership shared
+ mMutex.unlock();
+ mMutex.lock_shared();
mBlobCache->writeToFile();
+ mMutex.unlock_shared();
+ mMutex.lock();
}
}
void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /*description*/) {
ATRACE_NAME("ShaderCache::store");
- std::lock_guard<std::mutex> lock(mMutex);
+ std::lock_guard lock(mMutex);
mNumShadersCachedInRam++;
ATRACE_FORMAT("HWUI RAM cache: %d shaders", mNumShadersCachedInRam);
@@ -229,7 +234,7 @@ void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /
mSavePending = true;
std::thread deferredSaveThread([this]() {
usleep(mDeferredSaveDelayMs * 1000); // milliseconds to microseconds
- std::lock_guard<std::mutex> lock(mMutex);
+ std::lock_guard lock(mMutex);
// Store file on disk if there a new shader or Vulkan pipeline cache size changed.
if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
saveToDiskLocked();
@@ -245,11 +250,12 @@ void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /
void ShaderCache::onVkFrameFlushed(GrDirectContext* context) {
{
- std::lock_guard<std::mutex> lock(mMutex);
-
+ mMutex.lock_shared();
if (!mInitialized || !mTryToStorePipelineCache) {
+ mMutex.unlock_shared();
return;
}
+ mMutex.unlock_shared();
}
mInStoreVkPipelineInProgress = true;
context->storeVkPipelineCacheData();
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index f5506d60f811..2f91c778b8a0 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -19,8 +19,10 @@
#include <GrContextOptions.h>
#include <SkRefCnt.h>
#include <cutils/compiler.h>
+#include <ftl/shared_mutex.h>
+#include <utils/Mutex.h>
+
#include <memory>
-#include <mutex>
#include <string>
#include <vector>
@@ -99,20 +101,20 @@ private:
* this will do so, loading the serialized cache contents from disk if
* possible.
*/
- BlobCache* getBlobCacheLocked();
+ BlobCache* getBlobCacheLocked() REQUIRES(mMutex);
/**
* "validateCache" updates the cache to match the given identity. If the
* cache currently has the wrong identity, all entries in the cache are cleared.
*/
- bool validateCache(const void* identity, ssize_t size);
+ bool validateCache(const void* identity, ssize_t size) REQUIRES(mMutex);
/**
- * "saveToDiskLocked" attemps to save the current contents of the cache to
+ * "saveToDiskLocked" attempts to save the current contents of the cache to
* disk. If the identity hash exists, we will insert the identity hash into
* the cache for next validation.
*/
- void saveToDiskLocked();
+ void saveToDiskLocked() REQUIRES(mMutex);
/**
* "mInitialized" indicates whether the ShaderCache is in the initialized
@@ -122,7 +124,7 @@ private:
* the load and store methods will return without performing any cache
* operations.
*/
- bool mInitialized = false;
+ bool mInitialized GUARDED_BY(mMutex) = false;
/**
* "mBlobCache" is the cache in which the key/value blob pairs are stored. It
@@ -131,7 +133,7 @@ private:
* The blob cache contains the Android build number. We treat version mismatches as an empty
* cache (logic implemented in BlobCache::unflatten).
*/
- std::unique_ptr<FileBlobCache> mBlobCache;
+ std::unique_ptr<FileBlobCache> mBlobCache GUARDED_BY(mMutex);
/**
* "mFilename" is the name of the file for storing cache contents in between
@@ -140,7 +142,7 @@ private:
* empty string indicates that the cache should not be saved to or restored
* from disk.
*/
- std::string mFilename;
+ std::string mFilename GUARDED_BY(mMutex);
/**
* "mIDHash" is the current identity hash for the cache validation. It is
@@ -149,7 +151,7 @@ private:
* indicates that cache validation is not performed, and the hash should
* not be stored on disk.
*/
- std::vector<uint8_t> mIDHash;
+ std::vector<uint8_t> mIDHash GUARDED_BY(mMutex);
/**
* "mSavePending" indicates whether or not a deferred save operation is
@@ -159,7 +161,7 @@ private:
* contents to disk, unless mDeferredSaveDelayMs is 0 in which case saving
* is disabled.
*/
- bool mSavePending = false;
+ bool mSavePending GUARDED_BY(mMutex) = false;
/**
* "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
@@ -174,16 +176,16 @@ private:
unsigned int mDeferredSaveDelayMs = 4 * 1000;
/**
- * "mMutex" is the mutex used to prevent concurrent access to the member
+ * "mMutex" is the shared mutex used to prevent concurrent access to the member
* variables. It must be locked whenever the member variables are accessed.
*/
- mutable std::mutex mMutex;
+ mutable ftl::SharedMutex mMutex;
/**
* If set to "true", the next call to onVkFrameFlushed, will invoke
* GrCanvas::storeVkPipelineCacheData. This does not guarantee that data will be stored on disk.
*/
- bool mTryToStorePipelineCache = true;
+ bool mTryToStorePipelineCache GUARDED_BY(mMutex) = true;
/**
* This flag is used by "ShaderCache::store" to distinguish between shader data and
@@ -195,16 +197,16 @@ private:
* "mNewPipelineCacheSize" has the size of the new Vulkan pipeline cache data. It is used
* to prevent unnecessary disk writes, if the pipeline cache size has not changed.
*/
- size_t mNewPipelineCacheSize = -1;
+ size_t mNewPipelineCacheSize GUARDED_BY(mMutex) = -1;
/**
* "mOldPipelineCacheSize" has the size of the Vulkan pipeline cache data stored on disk.
*/
- size_t mOldPipelineCacheSize = -1;
+ size_t mOldPipelineCacheSize GUARDED_BY(mMutex) = -1;
/**
* "mCacheDirty" is true when there is new shader cache data, which is not saved to disk.
*/
- bool mCacheDirty = false;
+ bool mCacheDirty GUARDED_BY(mMutex) = false;
/**
* "sCache" is the singleton ShaderCache object.
@@ -221,7 +223,7 @@ private:
* interesting to keep track of how many shaders are stored in RAM. This
* class provides a convenient entry point for that.
*/
- int mNumShadersCachedInRam = 0;
+ int mNumShadersCachedInRam GUARDED_BY(mMutex) = 0;
friend class ShaderCacheTestUtils; // used for unit testing
};
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 16b35ffcabac..a5518eb9f854 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -405,8 +405,17 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
// If the previous frame was dropped we don't need to hold onto it, so
// just keep using the previous frame's structure instead
- if (!wasSkipped(mCurrentFrameInfo)) {
+ if (wasSkipped(mCurrentFrameInfo)) {
+ // Use the oldest skipped frame in case we skip more than a single frame
+ if (!mSkippedFrameInfo) {
+ mSkippedFrameInfo.emplace();
+ mSkippedFrameInfo->vsyncId =
+ mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+ mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ }
+ } else {
mCurrentFrameInfo = mJankTracker.startFrame();
+ mSkippedFrameInfo.reset();
}
mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
@@ -602,10 +611,18 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) {
if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
const auto inputEventId =
static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
- native_window_set_frame_timeline_info(
- mNativeSurface->getNativeWindow(), frameCompleteNr, vsyncId, inputEventId,
- mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime),
- solelyTextureViewUpdates);
+ const ANativeWindowFrameTimelineInfo ftl = {
+ .frameNumber = frameCompleteNr,
+ .frameTimelineVsyncId = vsyncId,
+ .inputEventId = inputEventId,
+ .startTimeNanos = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime),
+ .useForRefreshRateSelection = solelyTextureViewUpdates,
+ .skippedFrameVsyncId = mSkippedFrameInfo ? mSkippedFrameInfo->vsyncId
+ : UiFrameInfoBuilder::INVALID_VSYNC_ID,
+ .skippedFrameStartTimeNanos =
+ mSkippedFrameInfo ? mSkippedFrameInfo->startTime : 0,
+ };
+ native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), ftl);
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 5219b5757008..32ac5af94c14 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -366,6 +366,12 @@ private:
ColorMode mColorMode = ColorMode::Default;
float mTargetSdrHdrRatio = 1.f;
+
+ struct SkippedFrameInfo {
+ int64_t vsyncId;
+ int64_t startTime;
+ };
+ std::optional<SkippedFrameInfo> mSkippedFrameInfo;
};
} /* namespace renderthread */
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 7bcd45c6b643..9aa2e1db4461 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -49,7 +49,7 @@ public:
*/
static void reinitializeAllFields(ShaderCache& cache) {
ShaderCache newCache = ShaderCache();
- std::lock_guard<std::mutex> lock(cache.mMutex);
+ std::lock_guard lock(cache.mMutex), newLock(newCache.mMutex);
// By order of declaration
cache.mInitialized = newCache.mInitialized;
cache.mBlobCache.reset(nullptr);
@@ -72,7 +72,7 @@ public:
* manually, as seen in the "terminate" testing helper function.
*/
static void setSaveDelayMs(ShaderCache& cache, unsigned int saveDelayMs) {
- std::lock_guard<std::mutex> lock(cache.mMutex);
+ std::lock_guard lock(cache.mMutex);
cache.mDeferredSaveDelayMs = saveDelayMs;
}
@@ -81,7 +81,7 @@ public:
* Next call to "initShaderDiskCache" will load again the in-memory cache from disk.
*/
static void terminate(ShaderCache& cache, bool saveContent) {
- std::lock_guard<std::mutex> lock(cache.mMutex);
+ std::lock_guard lock(cache.mMutex);
if (saveContent) {
cache.saveToDiskLocked();
}
@@ -93,6 +93,7 @@ public:
*/
template <typename T>
static bool validateCache(ShaderCache& cache, std::vector<T> hash) {
+ std::lock_guard lock(cache.mMutex);
return cache.validateCache(hash.data(), hash.size() * sizeof(T));
}
@@ -108,7 +109,7 @@ public:
*/
static void waitForPendingSave(ShaderCache& cache, const int timeoutMs = 50) {
{
- std::lock_guard<std::mutex> lock(cache.mMutex);
+ std::lock_guard lock(cache.mMutex);
ASSERT_TRUE(cache.mSavePending);
}
bool saving = true;
@@ -123,7 +124,7 @@ public:
usleep(delayMicroseconds);
elapsedMilliseconds += (float)delayMicroseconds / 1000;
- std::lock_guard<std::mutex> lock(cache.mMutex);
+ std::lock_guard lock(cache.mMutex);
saving = cache.mSavePending;
}
}