summaryrefslogtreecommitdiff
path: root/quickstep
diff options
context:
space:
mode:
Diffstat (limited to 'quickstep')
-rw-r--r--quickstep/AndroidManifest-launcher.xml1
-rw-r--r--quickstep/res/drawable/ic_save_app_pair_left_right.xml47
-rw-r--r--quickstep/res/drawable/ic_save_app_pair_up_down.xml49
-rw-r--r--quickstep/res/drawable/taskbar_divider_button_expressive_theme.xml30
-rw-r--r--quickstep/res/layout/task.xml4
-rw-r--r--quickstep/res/layout/task_content_view.xml20
-rw-r--r--quickstep/res/layout/task_grouped.xml8
-rw-r--r--quickstep/res/layout/task_header_view.xml62
-rw-r--r--quickstep/res/layout/task_thumbnail.xml3
-rw-r--r--quickstep/res/layout/task_thumbnail_view_header.xml72
-rw-r--r--quickstep/res/values-af/strings.xml11
-rw-r--r--quickstep/res/values-am/strings.xml11
-rw-r--r--quickstep/res/values-ar/strings.xml11
-rw-r--r--quickstep/res/values-as/strings.xml11
-rw-r--r--quickstep/res/values-az/strings.xml11
-rw-r--r--quickstep/res/values-b+sr+Latn/strings.xml11
-rw-r--r--quickstep/res/values-be/strings.xml11
-rw-r--r--quickstep/res/values-bg/strings.xml11
-rw-r--r--quickstep/res/values-bn/strings.xml11
-rw-r--r--quickstep/res/values-bs/strings.xml11
-rw-r--r--quickstep/res/values-ca/strings.xml11
-rw-r--r--quickstep/res/values-cs/strings.xml11
-rw-r--r--quickstep/res/values-da/strings.xml11
-rw-r--r--quickstep/res/values-de/strings.xml11
-rw-r--r--quickstep/res/values-el/strings.xml11
-rw-r--r--quickstep/res/values-en-rAU/strings.xml11
-rw-r--r--quickstep/res/values-en-rCA/strings.xml11
-rw-r--r--quickstep/res/values-en-rGB/strings.xml11
-rw-r--r--quickstep/res/values-en-rIN/strings.xml11
-rw-r--r--quickstep/res/values-es-rUS/strings.xml13
-rw-r--r--quickstep/res/values-es/strings.xml11
-rw-r--r--quickstep/res/values-et/strings.xml11
-rw-r--r--quickstep/res/values-eu/strings.xml11
-rw-r--r--quickstep/res/values-fa/strings.xml11
-rw-r--r--quickstep/res/values-fi/strings.xml11
-rw-r--r--quickstep/res/values-fr-rCA/strings.xml11
-rw-r--r--quickstep/res/values-fr/strings.xml11
-rw-r--r--quickstep/res/values-gl/strings.xml11
-rw-r--r--quickstep/res/values-gu/strings.xml11
-rw-r--r--quickstep/res/values-hi/strings.xml11
-rw-r--r--quickstep/res/values-hr/strings.xml11
-rw-r--r--quickstep/res/values-hu/strings.xml11
-rw-r--r--quickstep/res/values-hy/strings.xml11
-rw-r--r--quickstep/res/values-in/strings.xml11
-rw-r--r--quickstep/res/values-is/strings.xml11
-rw-r--r--quickstep/res/values-it/strings.xml11
-rw-r--r--quickstep/res/values-iw/strings.xml11
-rw-r--r--quickstep/res/values-ja/strings.xml11
-rw-r--r--quickstep/res/values-ka/strings.xml11
-rw-r--r--quickstep/res/values-kk/strings.xml11
-rw-r--r--quickstep/res/values-km/strings.xml11
-rw-r--r--quickstep/res/values-kn/strings.xml11
-rw-r--r--quickstep/res/values-ko/strings.xml11
-rw-r--r--quickstep/res/values-ky/strings.xml11
-rw-r--r--quickstep/res/values-lo/strings.xml11
-rw-r--r--quickstep/res/values-lt/strings.xml11
-rw-r--r--quickstep/res/values-lv/strings.xml11
-rw-r--r--quickstep/res/values-mk/strings.xml11
-rw-r--r--quickstep/res/values-ml/strings.xml11
-rw-r--r--quickstep/res/values-mn/strings.xml11
-rw-r--r--quickstep/res/values-mr/strings.xml11
-rw-r--r--quickstep/res/values-ms/strings.xml11
-rw-r--r--quickstep/res/values-my/strings.xml11
-rw-r--r--quickstep/res/values-nb/strings.xml11
-rw-r--r--quickstep/res/values-ne/strings.xml11
-rw-r--r--quickstep/res/values-nl/strings.xml11
-rw-r--r--quickstep/res/values-or/strings.xml11
-rw-r--r--quickstep/res/values-pa/strings.xml11
-rw-r--r--quickstep/res/values-pl/strings.xml11
-rw-r--r--quickstep/res/values-pt-rPT/strings.xml11
-rw-r--r--quickstep/res/values-pt/strings.xml11
-rw-r--r--quickstep/res/values-ro/strings.xml11
-rw-r--r--quickstep/res/values-ru/strings.xml11
-rw-r--r--quickstep/res/values-si/strings.xml11
-rw-r--r--quickstep/res/values-sk/strings.xml11
-rw-r--r--quickstep/res/values-sl/strings.xml11
-rw-r--r--quickstep/res/values-sq/strings.xml11
-rw-r--r--quickstep/res/values-sr/strings.xml11
-rw-r--r--quickstep/res/values-sv/strings.xml11
-rw-r--r--quickstep/res/values-sw/strings.xml11
-rw-r--r--quickstep/res/values-ta/strings.xml13
-rw-r--r--quickstep/res/values-te/strings.xml11
-rw-r--r--quickstep/res/values-th/strings.xml11
-rw-r--r--quickstep/res/values-tl/strings.xml11
-rw-r--r--quickstep/res/values-tr/strings.xml11
-rw-r--r--quickstep/res/values-uk/strings.xml11
-rw-r--r--quickstep/res/values-ur/strings.xml11
-rw-r--r--quickstep/res/values-uz/strings.xml11
-rw-r--r--quickstep/res/values-vi/strings.xml11
-rw-r--r--quickstep/res/values-zh-rCN/strings.xml11
-rw-r--r--quickstep/res/values-zh-rHK/strings.xml11
-rw-r--r--quickstep/res/values-zh-rTW/strings.xml11
-rw-r--r--quickstep/res/values-zu/strings.xml11
-rw-r--r--quickstep/res/values/config.xml1
-rw-r--r--quickstep/res/values/dimens.xml15
-rw-r--r--quickstep/res/values/strings.xml10
-rw-r--r--quickstep/src/com/android/launcher3/QuickstepTransitionManager.java101
-rw-r--r--quickstep/src/com/android/launcher3/dagger/Modules.kt9
-rw-r--r--quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt15
-rw-r--r--quickstep/src/com/android/launcher3/statehandlers/DepthController.java97
-rw-r--r--quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt71
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/BarsLocationAnimatorHelper.kt34
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java46
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java25
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java26
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java5
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java84
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java63
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java145
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java10
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt9
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java15
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt3
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java15
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt7
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java94
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java130
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java2
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java25
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java17
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarView.java18
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java8
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacksFactory.kt3
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java15
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java59
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java83
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java5
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt5
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt9
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt132
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt21
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt14
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/growth/ActionPerformers.kt50
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt61
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt29
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt39
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt31
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt25
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java36
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java2
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java8
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java74
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHostProvider.kt48
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java10
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java58
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java125
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt7
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java7
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java7
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt11
-rw-r--r--quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java22
-rw-r--r--quickstep/src/com/android/quickstep/AspectRatioSystemShortcut.kt100
-rw-r--r--quickstep/src/com/android/quickstep/BaseContainerInterface.java2
-rw-r--r--quickstep/src/com/android/quickstep/DisplayModel.kt51
-rw-r--r--quickstep/src/com/android/quickstep/FallbackActivityInterface.java8
-rw-r--r--quickstep/src/com/android/quickstep/FallbackSwipeHandler.java3
-rw-r--r--quickstep/src/com/android/quickstep/FallbackWindowInterface.java21
-rw-r--r--quickstep/src/com/android/quickstep/HomeVisibilityState.kt10
-rw-r--r--quickstep/src/com/android/quickstep/InputConsumer.java20
-rw-r--r--quickstep/src/com/android/quickstep/InputConsumerUtils.kt120
-rw-r--r--quickstep/src/com/android/quickstep/LauncherActivityInterface.java14
-rw-r--r--quickstep/src/com/android/quickstep/LauncherBackAnimationController.java70
-rw-r--r--quickstep/src/com/android/quickstep/OverviewCommandHelper.kt97
-rw-r--r--quickstep/src/com/android/quickstep/OverviewComponentObserver.java21
-rw-r--r--quickstep/src/com/android/quickstep/RecentTasksList.java7
-rw-r--r--quickstep/src/com/android/quickstep/RemoteTargetGluer.java4
-rw-r--r--quickstep/src/com/android/quickstep/SystemDecorationChangeObserver.kt81
-rw-r--r--quickstep/src/com/android/quickstep/TaskAnimationManager.java23
-rw-r--r--quickstep/src/com/android/quickstep/TaskOverlayFactory.java9
-rw-r--r--quickstep/src/com/android/quickstep/TaskViewUtils.java4
-rw-r--r--quickstep/src/com/android/quickstep/TopTaskTracker.java143
-rw-r--r--quickstep/src/com/android/quickstep/TouchInteractionService.java155
-rw-r--r--quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java2
-rw-r--r--quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java5
-rw-r--r--quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java38
-rw-r--r--quickstep/src/com/android/quickstep/fallback/RecentsState.java34
-rw-r--r--quickstep/src/com/android/quickstep/fallback/RecentsStateUtils.kt33
-rw-r--r--quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt33
-rw-r--r--quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt9
-rw-r--r--quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt36
-rw-r--r--quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java8
-rw-r--r--quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java4
-rw-r--r--quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java7
-rw-r--r--quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java3
-rw-r--r--quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java55
-rw-r--r--quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.kt44
-rw-r--r--quickstep/src/com/android/quickstep/interaction/TutorialController.java57
-rw-r--r--quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt4
-rw-r--r--quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt2
-rw-r--r--quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt9
-rw-r--r--quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt4
-rw-r--r--quickstep/src/com/android/quickstep/recents/domain/usecase/GetThumbnailPositionUseCase.kt3
-rw-r--r--quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt85
-rw-r--r--quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt1
-rw-r--r--quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt15
-rw-r--r--quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt3
-rw-r--r--quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt4
-rw-r--r--quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt158
-rw-r--r--quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt32
-rw-r--r--quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt36
-rw-r--r--quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt106
-rw-r--r--quickstep/src/com/android/quickstep/util/AppPairsController.java19
-rw-r--r--quickstep/src/com/android/quickstep/util/BaseDepthController.java61
-rw-r--r--quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt3
-rw-r--r--quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java4
-rw-r--r--quickstep/src/com/android/quickstep/util/DesksUtils.kt8
-rw-r--r--quickstep/src/com/android/quickstep/util/DesktopTask.kt15
-rw-r--r--quickstep/src/com/android/quickstep/util/ExternalDisplays.kt16
-rw-r--r--quickstep/src/com/android/quickstep/util/GroupTask.kt2
-rw-r--r--quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java7
-rw-r--r--quickstep/src/com/android/quickstep/util/SplitAnimationController.kt28
-rw-r--r--quickstep/src/com/android/quickstep/util/SplitSelectStateController.java2
-rw-r--r--quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java23
-rw-r--r--quickstep/src/com/android/quickstep/views/BlurUtils.kt63
-rw-r--r--quickstep/src/com/android/quickstep/views/DesktopTaskView.kt73
-rw-r--r--quickstep/src/com/android/quickstep/views/GroupedTaskView.kt27
-rw-r--r--quickstep/src/com/android/quickstep/views/IconAppChipView.kt20
-rw-r--r--quickstep/src/com/android/quickstep/views/LauncherRecentsView.java31
-rw-r--r--quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt211
-rw-r--r--quickstep/src/com/android/quickstep/views/RecentsView.java287
-rw-r--r--quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt120
-rw-r--r--quickstep/src/com/android/quickstep/views/TaskContainer.kt21
-rw-r--r--quickstep/src/com/android/quickstep/views/TaskMenuView.kt9
-rw-r--r--quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt (renamed from quickstep/src/com/android/quickstep/views/TaskHeaderView.kt)24
-rw-r--r--quickstep/src/com/android/quickstep/views/TaskView.kt182
-rw-r--r--quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java21
-rw-r--r--quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt43
-rw-r--r--quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt124
-rw-r--r--quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt80
-rw-r--r--quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt60
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt2
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt3
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.kt154
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt3
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt5
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt27
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt3
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java10
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt83
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java72
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/TaskAnimationManagerTest.java3
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/fallback/RecentsStateUtilsTest.kt66
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java6
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/LandscapePagedViewHandlerTest.kt14
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt14
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt3
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/recents/domain/usecase/GetThumbnailPositionUseCaseTest.kt34
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt150
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt35
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/util/AppPairsControllerTest.kt11
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/util/DesktopTaskTest.kt21
-rw-r--r--quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt3
-rw-r--r--quickstep/tests/src/com/android/launcher3/statehandlers/DepthControllerTest.kt105
-rw-r--r--quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java265
-rw-r--r--quickstep/tests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt (renamed from quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt)37
-rw-r--r--quickstep/tests/src/com/android/quickstep/AspectRatioSystemShortcutTests.kt285
-rw-r--r--quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt7
-rw-r--r--quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt2
-rw-r--r--quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java44
-rw-r--r--quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt183
260 files changed, 5247 insertions, 3089 deletions
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 80d8154ba1..d6aa886d8c 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -57,6 +57,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.SHOW_WORK_APPS" />
+ <action android:name="android.intent.action.ALL_APPS" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
diff --git a/quickstep/res/drawable/ic_save_app_pair_left_right.xml b/quickstep/res/drawable/ic_save_app_pair_left_right.xml
index b104f44b76..509bc98f65 100644
--- a/quickstep/res/drawable/ic_save_app_pair_left_right.xml
+++ b/quickstep/res/drawable/ic_save_app_pair_left_right.xml
@@ -1,28 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
-
<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:pathData="M8.5,4.5H3.5C2.4,4.5 1.5,5.4 1.5,6.5V18.5C1.5,19.6 2.4,20.5 3.5,20.5H8.5C9.6,20.5 10.5,19.6 10.5,18.5V6.5C10.5,5.4 9.6,4.5 8.5,4.5ZM8.5,18.5H3.5V6.5H8.5V18.5ZM14.5,6.5H19.5V13.5H21.5V6.5C21.5,5.4 20.6,4.5 19.5,4.5H14.5C13.4,4.5 12.5,5.4 12.5,6.5V18.5C12.5,19.6 13.4,20.5 14.5,20.5H15.5V18.5H14.5V6.5ZM20.5,14.5V16.5H22.5V18.5H20.5V20.5H18.5V18.5H16.5V16.5H18.5V14.5H20.5Z"
- android:fillColor="#48473A"
- android:fillType="evenOdd"/>
+ android:width="20dp"
+ android:height="20dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M744,816L720,816Q704.7,816 694.35,805.71Q684,795.42 684,780.21Q684,765 694.35,754.5Q704.7,744 720,744L744,744L744,720Q744,704.7 754.29,694.35Q764.58,684 779.79,684Q795,684 805.5,694.35Q816,704.7 816,720L816,744L840,744Q855.3,744 865.65,754.29Q876,764.58 876,779.79Q876,795 865.65,805.5Q855.3,816 840,816L816,816L816,840Q816,855.3 805.71,865.65Q795.42,876 780.21,876Q765,876 754.5,865.65Q744,855.3 744,840L744,816ZM215.74,816Q186,816 165,794.85Q144,773.7 144,744L144,216Q144,186.3 165.18,165.15Q186.35,144 216.09,144L360.26,144Q390,144 411,165.15Q432,186.3 432,216L432,744Q432,773.7 410.82,794.85Q389.65,816 359.91,816L215.74,816ZM616,816Q581,816 554.5,796Q528,776 528,743.95L528,216Q528,186.3 549.18,165.15Q570.35,144 600.09,144L744.26,144Q774,144 795,165.15Q816,186.3 816,216L816,616Q807,614 798,613Q789,612 780,612Q709.79,612 660.9,661Q612,710 612,780Q612,789 613,798Q614,807 616,816Z" />
</vector>
diff --git a/quickstep/res/drawable/ic_save_app_pair_up_down.xml b/quickstep/res/drawable/ic_save_app_pair_up_down.xml
index 86f110ce5e..282751c84c 100644
--- a/quickstep/res/drawable/ic_save_app_pair_up_down.xml
+++ b/quickstep/res/drawable/ic_save_app_pair_up_down.xml
@@ -1,28 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
-
<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:pathData="M18,2L6,2C4.9,2 4,2.9 4,4L4,9C4,10.1 4.9,11 6,11L18,11C19.1,11 20,10.1 20,9L20,4C20,2.9 19.1,2 18,2ZM18,9L6,9L6,4L18,4L18,9ZM18,13L6,13C4.9,13 4,13.9 4,15L4,20C4,21.1 4.9,22 6,22L13,22L13,20L6,20L6,15L18,15L18,16L20,16L20,15C20,13.9 19.1,13 18,13ZM16,17L18,17L18,19L20,19L20,21L18,21L18,23L16,23L16,21L14,21L14,19L16,19L16,17Z"
- android:fillColor="#48473A"
- android:fillType="evenOdd"/>
-</vector>
+ android:width="20dp"
+ android:height="20dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M744,816L720,816Q704.7,816 694.35,805.71Q684,795.42 684,780.21Q684,765 694.35,754.5Q704.7,744 720,744L744,744L744,720Q744,704.7 754.29,694.35Q764.58,684 779.79,684Q795,684 805.5,694.35Q816,704.7 816,720L816,744L840,744Q855.3,744 865.65,754.29Q876,764.58 876,779.79Q876,795 865.65,805.5Q855.3,816 840,816L816,816L816,840Q816,855.3 805.71,865.65Q795.42,876 780.21,876Q765,876 754.5,865.65Q744,855.3 744,840L744,816ZM216,432Q183,432 163.5,412.5Q144,393 144,360L144,216Q144,183 163.5,163.5Q183,144 216,144L744,144Q777,144 796.5,163.5Q816,183 816,216L816,360Q816,393 796.5,412.5Q777,432 744,432L216,432ZM215.62,816Q183,816 163.5,796.5Q144,777 144,744L144,600Q144,567 163.5,547.5Q183,528 216,528L744,528Q777,528 796.5,547.5Q816,567 816,600L816,616Q808,614 798.06,613Q788.13,612 780,612Q709.44,612 660.72,661.5Q612,711 612,780Q612,789.94 612.5,798.97Q613,808 615,816L215.62,816Z" />
+</vector> \ No newline at end of file
diff --git a/quickstep/res/drawable/taskbar_divider_button_expressive_theme.xml b/quickstep/res/drawable/taskbar_divider_button_expressive_theme.xml
new file mode 100644
index 0000000000..7fc61fc4a7
--- /dev/null
+++ b/quickstep/res/drawable/taskbar_divider_button_expressive_theme.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="52dp"
+ android:height="52dp"
+ android:viewportHeight="52"
+ android:viewportWidth="52">
+ <group>
+ <path
+ android:fillColor="@color/taskbar_divider_background"
+ android:pathData="M26,14L26,38"
+ android:strokeColor="@color/taskbar_divider_background"
+ android:strokeLineCap="round"
+ android:strokeWidth="3" />
+ </group>
+</vector> \ No newline at end of file
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 4abfbbe152..3aac1b63f9 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -29,8 +29,8 @@
launcher:hoverBorderColor="@color/materialColorPrimary">
<ViewStub
- android:id="@+id/task_content_view"
- android:inflatedId="@id/task_content_view"
+ android:id="@+id/snapshot"
+ android:inflatedId="@id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent" />
diff --git a/quickstep/res/layout/task_content_view.xml b/quickstep/res/layout/task_content_view.xml
deleted file mode 100644
index 9055ccd745..0000000000
--- a/quickstep/res/layout/task_content_view.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2025 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<com.android.quickstep.task.thumbnail.TaskContentView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/task_content_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" /> \ No newline at end of file
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
index a7c48561c8..3e6f5eda69 100644
--- a/quickstep/res/layout/task_grouped.xml
+++ b/quickstep/res/layout/task_grouped.xml
@@ -34,14 +34,14 @@
launcher:hoverBorderColor="@color/materialColorPrimary">
<ViewStub
- android:id="@+id/task_content_view"
- android:inflatedId="@id/task_content_view"
+ android:id="@+id/snapshot"
+ android:inflatedId="@id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ViewStub
- android:id="@+id/bottomright_task_content_view"
- android:inflatedId="@id/bottomright_task_content_view"
+ android:id="@+id/bottomright_snapshot"
+ android:inflatedId="@id/bottomright_snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent" />
diff --git a/quickstep/res/layout/task_header_view.xml b/quickstep/res/layout/task_header_view.xml
deleted file mode 100644
index 849153fd9e..0000000000
--- a/quickstep/res/layout/task_header_view.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright (C) 2025 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.quickstep.views.TaskHeaderView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/task_header_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/task_thumbnail_header_padding_top_bottom"
- android:paddingBottom="@dimen/task_thumbnail_header_padding_top_bottom"
- android:paddingStart="@dimen/task_thumbnail_header_padding_start_end"
- android:paddingEnd="@dimen/task_thumbnail_header_padding_start_end"
- android:background="@drawable/task_thumbnail_header_bg">
- <ImageView
- android:id="@+id/header_app_icon"
- android:layout_width="@dimen/task_thumbnail_header_icon_size"
- android:layout_height="@dimen/task_thumbnail_header_icon_size"
- android:layout_marginStart="@dimen/task_thumbnail_header_margin_between_views"
- android:layout_marginEnd="@dimen/task_thumbnail_header_margin_between_views"
- android:contentDescription="@string/header_app_icon_description"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
- <TextView
- android:id="@+id/header_app_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/task_thumbnail_header_margin_between_views"
- android:layout_marginEnd="@dimen/task_thumbnail_header_margin_between_views"
- android:maxLines="1"
- android:text="@string/header_default_app_title"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toEndOf="@id/header_app_icon"
- app:layout_constraintTop_toTopOf="parent" />
- <ImageButton
- android:id="@+id/header_close_button"
- android:layout_width="@dimen/task_thumbnail_header_icon_size"
- android:layout_height="@dimen/task_thumbnail_header_icon_size"
- android:layout_marginStart="@dimen/task_thumbnail_header_margin_between_views"
- android:layout_marginEnd="@dimen/task_thumbnail_header_margin_between_views"
- android:background="@null"
- android:contentDescription="@string/header_close_icon_description"
- android:src="@drawable/task_header_close_button"
- android:tint="@android:color/darker_gray"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="1"
- app:layout_constraintStart_toEndOf="@id/header_app_title"
- app:layout_constraintTop_toTopOf="parent" />
-</com.android.quickstep.views.TaskHeaderView>
diff --git a/quickstep/res/layout/task_thumbnail.xml b/quickstep/res/layout/task_thumbnail.xml
index 8280e13f5b..3b966159d1 100644
--- a/quickstep/res/layout/task_thumbnail.xml
+++ b/quickstep/res/layout/task_thumbnail.xml
@@ -17,8 +17,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/snapshot"
android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" >
+ android:layout_height="match_parent" >
<com.android.quickstep.views.FixedSizeImageView
android:id="@+id/task_thumbnail"
diff --git a/quickstep/res/layout/task_thumbnail_view_header.xml b/quickstep/res/layout/task_thumbnail_view_header.xml
new file mode 100644
index 0000000000..70e4a42b41
--- /dev/null
+++ b/quickstep/res/layout/task_thumbnail_view_header.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.quickstep.views.TaskThumbnailViewHeader
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/task_thumbnail_view_header"
+ android:background="@drawable/task_thumbnail_header_bg">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/task_thumbnail_header_height"
+ android:layout_marginStart="@dimen/task_thumbnail_header_margin_edge"
+ android:layout_marginEnd="@dimen/task_thumbnail_header_margin_edge"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ <ImageView
+ android:id="@+id/header_app_icon"
+ android:contentDescription="@string/header_app_icon_description"
+ android:layout_width="@dimen/task_thumbnail_header_icon_size"
+ android:layout_height="@dimen/task_thumbnail_header_icon_size"
+ android:layout_marginEnd="@dimen/task_thumbnail_header_margin_between_views"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/header_app_title"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintVertical_bias="0.5"
+ app:layout_constraintHorizontal_chainStyle="spread_inside" />
+ <TextView
+ android:id="@+id/header_app_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/task_thumbnail_header_margin_between_views"
+ android:layout_marginEnd="@dimen/task_thumbnail_header_margin_between_views"
+ android:text="@string/header_default_app_title"
+ app:layout_constraintStart_toEndOf="@id/header_app_icon"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintVertical_bias="0.5" />
+ <ImageButton
+ android:id="@+id/header_close_button"
+ android:contentDescription="@string/header_close_icon_description"
+ android:layout_width="@dimen/task_thumbnail_header_icon_size"
+ android:layout_height="@dimen/task_thumbnail_header_icon_size"
+ android:layout_marginStart="@dimen/task_thumbnail_header_margin_between_views"
+ android:src="@drawable/task_header_close_button"
+ android:tint="@android:color/darker_gray"
+ android:background="@null"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintVertical_bias="0.5" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.quickstep.views.TaskThumbnailViewHeader>
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 83e6f79ce0..025a7d07c4 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Skuif na links bo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Skuif na regs onder"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Maak app as ’n borrel oop"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Onlangse apps"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Onlangse applys"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{meer app}other{meer apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Werkskerm"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> en <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, item <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> van <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Borrel"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Oorvloei"</string>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index d5967346aa..fc4c4c46d3 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ወደ ላይ/ግራ ይውሰዱ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ወደ ታች/ቀኝ ይውሰዱ"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"መተግበሪያን እንደ ዓረፋ ይክፈቱ"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"የቅርብ ጊዜ መተግበሪያዎች"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"የቅርብ ጊዜ መተግበሪያ ዝርዝር"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ተጨማሪ መተግበሪያ}one{ተጨማሪ መተግበሪያ}other{ተጨማሪ መተግበሪያዎች}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ዴስክቶፕ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> እና <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>፣ ንጥል <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> ከ<xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"አረፋ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ትርፍ ፍሰት"</string>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index ec379830c0..17bcbee55b 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"الانتقال إلى يمين الشاشة أو أعلاها"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"الانتقال إلى يسار الشاشة أو أسفلها"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"فتح التطبيق كفقاعة"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"التطبيقات المستخدَمة مؤخرًا"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"قائمة التطبيقات المستخدَمة مؤخرًا"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{تطبيق واحد آخر}zero{تطبيق آخر}two{تطبيقان آخران}few{تطبيقات أخرى}many{تطبيقًا آخر}other{تطبيق آخر}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"وضع الكمبيوتر المكتبي"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"\"<xliff:g id="APP_NAME_1">%1$s</xliff:g>\" و\"<xliff:g id="APP_NAME_2">%2$s</xliff:g>\""</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"‫<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، العنصر رقم <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> من إجمالي <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"فقاعة"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"القائمة الكاملة"</string>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index 9cadc10d25..0f8126beb4 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ওপৰৰ বাঁওফাললৈ নিয়ক"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"তলৰ সোঁফাললৈ নিয়ক"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"বাবল হিচাপে এপ্‌টো খোলক"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"শেহতীয়া এপ্‌সমূহ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"শেহতীয়া এপৰ সূচী"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{অধিক এপ্‌}one{অধিক এপ্‌}other{অধিক এপ্‌}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ডেস্কটপ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> আৰু <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>টাৰ ভিতৰত <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>তম বস্তু"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"বাবল"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"অ’ভাৰফ্ল’"</string>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index bf34d27520..f88a48c54e 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuxarı/sola köçürün"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Aşağı/sağa köçürün"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Tətbiqi yumrucuq kimi açın"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Son tətbiqlər"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Son tətbiq siyahısı"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{əlavə tətbiq}other{əlavə tətbiq}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Masaüstü"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> və <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, element <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>/<xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Yumrucuq"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Kənara çıxma"</string>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 14724e95eb..8b60c14da3 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premesti gore levo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premesti dole desno"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otvori aplikaciju kao oblačić"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Nedavne aplikacije"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lista nedavnih aplikacija"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Računar"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, stavka <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> od <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Oblačić"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Preklopni"</string>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 5f55b41df2..f0d4ee4a0d 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перамясціць уверх/улева"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перамясціць уніз/управа"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Адкрыць праграму ва ўсплывальным акне"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Нядаўнія праграмы"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Спіс нядаўніх праграм"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{даступная праграма}one{даступная праграма}few{даступныя праграмы}many{даступных праграм}other{даступнай праграмы}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Працоўны стол"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> і <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, элемент <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> з <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Бурбалкі"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Меню з пашырэннем"</string>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 871501f64e..10401866a1 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Преместване горе/вляво"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Преместване долу/вдясно"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Отваряне на приложението като балонче"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Скорошни приложения"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Списък със скорошни приложения"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{допълнително приложение}other{допълнителни приложения}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Режим за настолни компютри"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, елемент <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> от <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Балонче"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Препълване"</string>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index a1e30bb00d..2c700670d5 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"উপরে/বাঁদিকে সরান"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"নিচে/ডানদিকে সরান"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"বাবল হিসেবে অ্যাপ খুলুন"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"সম্প্রতি ব্যবহৃত অ্যাপ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"সাম্প্রতিক অ্যাপের তালিকা"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{আরও অ্যাপ}one{আরও অ্যাপ}other{আরও অ্যাপ}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ডেস্কটপ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ও <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>টি টাস্কের মধ্যে <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> নম্বর আইটেম"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"বাবল"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ওভারফ্লো"</string>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 57aa3c3e30..45bde4ca18 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore lijevo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje desno"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otvori aplikaciju kao oblačić"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Nedavne aplikacije"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Liste nedavnih aplikacija"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Računar"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>. stavka od <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Oblačić"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Preklopni meni"</string>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index e729ff5639..07a7349406 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mou a la part superior o a l\'esquerra"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mou a la part inferior o a la dreta"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Obre l\'aplicació com a bombolla"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Aplicacions recents"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Llista d\'aplicacions recents"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicació més}other{aplicacions més}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Escriptori"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, element <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> de <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bombolla"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Desbordament"</string>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 5106ae3119..98ed9346df 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Přesunout doleva nahoru"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Přesunout doprava dolů"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otevřít aplikaci jako bublinu"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Poslední aplikace"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Seznam posledních aplikací"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{další aplikace}few{další aplikace}many{další aplikace}other{dalších aplikací}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Počítač"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> a <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, položka <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> z <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bublina"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Rozbalovací nabídka"</string>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 1328da9132..d4ec1e681c 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flyt til toppen eller venstre side"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flyt til bunden eller højre side"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Åbn appen som en boble"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Seneste apps"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Liste over seneste apps"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{yderligere app}one{yderligere app}other{yderligere apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Computertilstand"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, element <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> af <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Boble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overløb"</string>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 2074d5e27b..e3ab11cd95 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Nach oben / Nach links verschieben"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Nach unten / Nach rechts verschieben"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"App als Bubble öffnen"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Zuletzt aktive Apps"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Liste der zuletzt aktiven Apps"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{weitere App}other{weitere Apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktopmodus"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> und <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, Element <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> von <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Weitere Optionen"</string>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 8951e20f5d..eae51d1535 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Μετακίνηση επάνω/αριστερά"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Μετακίνηση κάτω/δεξιά"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Άνοιγμα εφαρμογής σε συννεφάκι"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Πρόσφατες εφαρμογές"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Λίστα πρόσφατων εφαρμογών"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ακόμη εφαρμογή}other{ακόμη εφαρμογές}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Υπολογιστής"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> και <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, στοιχείο <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> από <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Συννεφάκι"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Υπερχείλιση"</string>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index 18e59840a0..6397fa5e8c 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Open app as a bubble"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Recent apps"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Recent app list"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, item <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> of <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index 8f52c66954..fcd7c66490 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -140,15 +140,14 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Open app as a bubble"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Recent apps"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Recent app list"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
- <skip />
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, item <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> of <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <string name="quick_switch_scroll_arrow_left" msgid="6527033155534184309">"Scroll left"</string>
+ <string name="quick_switch_scroll_arrow_right" msgid="7319779291086916348">"Scroll right"</string>
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
<string name="bubble_bar_bubble_description" msgid="1882466152448446446">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> from <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index 18e59840a0..6397fa5e8c 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Open app as a bubble"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Recent apps"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Recent app list"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, item <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> of <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index 18e59840a0..6397fa5e8c 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Move to top/left"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Move to bottom/right"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Open app as a bubble"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Recent apps"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Recent app list"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{more app}other{more apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> and <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, item <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> of <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index d909af12aa..703c434b77 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -96,7 +96,7 @@
<string name="action_share" msgid="2648470652637092375">"Compartir"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
<string name="action_split" msgid="2098009717623550676">"Pantalla dividida"</string>
- <string name="action_save_app_pair" msgid="5974823919237645229">"Guardar vinculación"</string>
+ <string name="action_save_app_pair" msgid="5974823919237645229">"Guardar grupo de apps"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Presiona otra app para usar la pantalla dividida"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Elige otra app para usar la pantalla dividida"</string>
<string name="toast_split_select_app_cancel" msgid="1939025102486630426">"Cancelar"</string>
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover a la parte superior o izquierda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover a la parte inferior o derecha"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir app como burbuja"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Apps recientes"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lista de apps recientes"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app más}other{apps más}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Escritorio"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> y <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, elemento <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> de <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbuja"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ampliada"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 016e5794bb..56489dac40 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover arriba/a la izquierda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover abajo/a la derecha"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir aplicación como burbuja"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Aplicaciones recientes"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lista de aplicaciones recientes"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app más}other{apps más}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Ordenador"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> y <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, elemento <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> de <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbuja"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Menú adicional"</string>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index ecf59afd75..c37268221d 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Teisalda üles/vasakule"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Teisalda alla/paremale"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Rakenduse avamine mullina"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Hiljutised rakendused"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Hiljutiste rakenduste loend"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{rakendus veel}other{rakendust veel}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Lauaarvuti"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ja <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, üksus <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>/<xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Mull"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ületäide"</string>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 90748c09ad..77b6b6decd 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Eraman gora, ezkerretara"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Eraman behera, eskuinetara"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Ireki aplikazioa burbuila gisa"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Azkenaldiko aplikazioak"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Azkenaldiko aplikazioen zerrenda"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikazio gehiago}other{aplikazio gehiago}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Ordenagailua"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> eta <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> elementutatik <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>garrena"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbuila"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Luzapena"</string>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index ee9ed1df98..7b9b719c10 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"انتقال به بالا/ چپ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"انتقال به پایین/ راست"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"باز کردن برنامه به‌صورت حبابک"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"برنامه‌های اخیر"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"فهرست برنامه‌های اخیر"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{برنامه دیگر}one{برنامه دیگر}other{برنامه دیگر}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"رایانه"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> و <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"‫<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، مورد <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> از <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"حبابک"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"سرریز"</string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index f681e0d2e7..03736047d0 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Siirrä ylös tai vasemmalle"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Siirrä alas tai oikealle"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Avaa sovellus kuplana"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Viimeaikaiset sovellukset"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Viimeaikaisten sovellusten lista"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{muu sovellus}other{muuta sovellusta}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Tietokone"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ja <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, kohde <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>/<xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Kupla"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ylivuoto"</string>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 5b1f1498a8..fcf03bedf3 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer vers le coin supérieur gauche de l\'écran"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer vers le coin inférieur droit de l\'écran"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Ouvrir l\'appli sous forme de bulle"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Applis récentes"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Liste des applis récentes"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{autre appli}one{autre appli}other{autres applis}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Bureau"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> et <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, élément <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> de <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bulle"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Bulle à développer"</string>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 95b448d415..9a14df83c9 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Déplacer en haut ou à gauche"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Déplacer en bas ou à droite"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Ouvrir l\'appli sous forme de bulle"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Applis récentes"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Liste des applis récentes"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{autre application}one{autre application}other{autres applications}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Mode ordinateur"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> et <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"Élément <xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> sur <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> : <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bulle"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Dépassement"</string>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index e59b4519e9..9a3cfa01c5 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover á parte superior ou á esquerda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover á parte inferior ou á dereita"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir aplicación como unha burbulla"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Aplicacións recentes"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lista de aplicacións recentes"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicación máis}other{aplicacións máis}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Escritorio"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, artigo <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> de <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbulla"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Menú adicional"</string>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index b0238d0145..736ce925da 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"સૌથી ઉપર ડાબી બાજુએ ખસેડો"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"સૌથી નીચે જમણી બાજુએ ખસેડો"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"બબલ તરીકે ઍપ ખોલો"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"તાજેતરની ઍપ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"તાજેતરની ઍપની સૂચિ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{વધુ ઍપ}one{વધુ ઍપ}other{વધુ ઍપ}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ડેસ્કટૉપ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> અને <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>માંથી આઇટમ <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"બબલ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ઓવરફ્લો"</string>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 6aba44cdf0..8c8d7ae029 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ऊपर/बाईं तरफ़ ले जाएं"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"नीचे/दाईं तरफ़ ले जाएं"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"ऐप्लिकेशन को बबल के तौर पर खोलें"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन की सूची"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ज़्यादा ऐप्लिकेशन}one{ज़्यादा ऐप्लिकेशन}other{ज़्यादा ऐप्लिकेशन}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"डेस्कटॉप"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> और <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> में से <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> आइटम"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"बबल"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ओवरफ़्लो"</string>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 963ce57464..2fb667909c 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premjesti gore/lijevo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premjesti dolje/desno"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otvori aplikaciju kao oblačić"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Nedavne aplikacije"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Popis nedavnih aplikacija"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}few{dodatne aplikacije}other{dodatnih aplikacija}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Računalo"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, stavka <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> od <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Oblačić"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Dodatni izbornik"</string>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 093c92d797..6584eafa75 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mozgatás felülre vagy a bal oldalra"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mozgatás alulra vagy a jobb oldalra"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Alkalmazás megnyitása buborékként"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Legutóbbi alkalmazások"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Legutóbbi alkalmazások listája"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{további alkalmazás}other{további alkalmazás}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Asztali"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> és <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>. elem, összesen ennyiből: <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Buborék"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Túlcsordulás"</string>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 0fa5d2d02b..e418e8ee5b 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Տեղափոխել վերևի ձախ անկյուն"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Տեղափոխել ներքևի աջ անկյուն"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Բացել հավելվածը պղպջակի ձևով"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Վերջերս բացված հավելվածներ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Վերջերս բացված հավելվածների ցանկ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{լրացուցիչ հավելված}one{լրացուցիչ հավելված}other{լրացուցիչ հավելված}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Համակարգիչ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> և <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, տարր <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>՝ <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>-ից"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Ամպիկ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Լրացուցիչ ընտրացանկ"</string>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index fe28340343..0fda663a0e 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pindahkan ke atas/kiri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pindahkan ke bawah/kanan"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Buka aplikasi sebagai balon"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Aplikasi yang baru dibuka"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Daftar aplikasi yang baru dibuka"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikasi lainnya}other{aplikasi lainnya}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dan <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, item <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> dari <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Balon"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Tambahan"</string>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index a0657ddad7..abfc7cf22a 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Færa efst/til vinstri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Færa neðst/til hægri"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Opna forrit sem blöðru"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Nýleg forrit"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Listi yfir nýleg forrit"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{forrit til viðbótar}one{forrit til viðbótar}other{forrit til viðbótar}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Skjáborð"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, atriði <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> af <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Blaðra"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Yfirflæði"</string>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index bc795e4182..00529b744a 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sposta in alto/a sinistra"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sposta in basso/a destra"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Apri l\'app come fumetto"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"App recenti"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Elenco app recenti"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{altra app}other{altre app}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, elemento <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> di <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Fumetto"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Extra"</string>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 3f5ddfc26b..32783628fd 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"העברה לפינה השמאלית/העליונה"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"העברה לפינה הימנית/התחתונה"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"פתיחת האפליקציה בבועה"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"אפליקציות אחרונות"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"רשימת האפליקציות האחרונות"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{אפליקציה נוספת}one{אפליקציות נוספות}two{אפליקציות נוספות}other{אפליקציות נוספות}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"מחשב"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ו-<xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"‫<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, פריט <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> מתוך <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"בועה"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"אפשרויות נוספות"</string>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index d6f64a59b3..596e104363 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"上 / 左に移動"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"下 / 右に移動"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"アプリをバブルとして開く"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"最近使ったアプリ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"最近使ったアプリの一覧"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個のその他のアプリ}other{個のその他のアプリ}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"デスクトップ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> と <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>、アイテム <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>/<xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ふきだし"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"オーバーフロー"</string>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 1ea1eeab7e..ac46836c49 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ზემოთ/მარცხნივ გადატანა"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ქვემოთ/მარჯვნივ გადატანა"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"აპის გახსნა ბუშტის სახით"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"ბოლოდროინდელი აპები"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"ბოლოდროინდელი აპების სია"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{სხვა აპი}other{სხვა აპი}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"დესკტოპი"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> და <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>-ე ერთეული <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>-დან"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ბუშტი"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"გადავსება"</string>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index b06ac371d6..c2861d6c00 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жоғары/солға жылжыту"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмен/оңға жылжыту"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Қолданбаны қалқыма терезе түрінде ашу"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Соңғы қолданбалар"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Соңғы қолданбалар тізімі"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{қосымша қолданба}other{қосымша қолданба}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Компьютер"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> және <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>/<xliff:g id="TOTAL_TASKS">%3$d</xliff:g> элемент"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Қалқыма терезе"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Қосымша мәзір"</string>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 6686851340..799bcda688 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ផ្លាស់ទីទៅខាងលើ/ឆ្វេង"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ផ្លាស់ទីទៅខាងក្រោម/ស្ដាំ"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"បើកកម្មវិធីជាផ្ទាំងសារ"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"កម្មវិធី​ថ្មីៗ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"បញ្ជីកម្មវិធីថ្មីៗ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{កម្មវិធីច្រើនទៀត}other{កម្មវិធីច្រើនទៀត}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"អេក្រង់ដើម"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> និង <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> ធាតុទី <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> នៃ <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ផ្ទាំងសារ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ម៉ឺនុយបន្ថែម"</string>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 3bb3d46a22..c88aed2e19 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ಮೇಲಿನ/ಎಡಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ಕೆಳಗಿನ/ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"ಆ್ಯಪ್ ಅನ್ನು ಬಬಲ್ ಆಗಿ ತೆರೆಯಿರಿ"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳು"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್ ಪಟ್ಟಿ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ಹೆಚ್ಚಿನ ಆ್ಯಪ್‌}one{ಹೆಚ್ಚಿನ ಆ್ಯಪ್‌ಗಳು}other{ಹೆಚ್ಚಿನ ಆ್ಯಪ್‌ಗಳು}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ಡೆಸ್ಕ್‌ಟಾಪ್"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ಮತ್ತು <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> ರಲ್ಲಿ <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> ಐಟಂ"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ಬಬಲ್"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ಓವರ್‌ಫ್ಲೋ"</string>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 367908f99d..5df9fdd548 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"상단/왼쪽으로 이동"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"하단/오른쪽으로 이동"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"앱을 대화창으로 열기"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"최근 앱"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"최근 앱 목록"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{추가 앱}other{추가 앱}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"데스크톱"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> 및 <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>개 중 <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>번째 항목"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"풍선"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"더보기"</string>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 834e38fac4..049da10fae 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Жогорку/сол бурчка жылдыруу"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Төмөнкү/оң бурчка жылдыруу"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Колдонмону көбүкчө катары ачуу"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Акыркы колдонмолор"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Акыркы колдонмолордун тизмеси"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{колдонмо бар}other{колдонмо бар}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Компьютер"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> жана <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> ичинен <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> нерсе"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Көбүкчө"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Кошумча меню"</string>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 80b745b2b6..9eb169adf7 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ຍ້າຍໄປຊ້າຍ/ເທິງ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ຍ້າຍໄປຂວາ/ລຸ່ມ"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"ເປີດແອັບເປັນຟອງ"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"ແອັບຫຼ້າສຸດ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"ລາຍຊື່ແອັບຫຼ້າສຸດ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ແອັບເພີ່ມເຕີມ}other{ແອັບເພີ່ມເຕີມ}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ເດັສທັອບ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ແລະ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, ລາຍການທີ <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> ຈາກທັງໝົດ <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ຟອງ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ລາຍການເພີ່ມເຕີມ"</string>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index bb1fff342e..2cbdc8e4f4 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Perkelti aukštyn, kairėn"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Perkelti žemyn, dešinėn"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Atidaryti programą kaip burbulą"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Naujausios programos"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Naujausių programų sąrašas"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{papildoma programa}one{papildoma programa}few{papildomos programos}many{papildomos programos}other{papildomų programų}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Stalinis kompiuteris"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"„<xliff:g id="APP_NAME_1">%1$s</xliff:g>“ ir „<xliff:g id="APP_NAME_2">%2$s</xliff:g>“"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> element. iš <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbulas"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Perpildymas"</string>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index c3c21a69cb..f8cda4a2f2 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Pārvietot uz augšējo/kreiso stūri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pārvietot uz apakšējo/labo stūri"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Atvērt lietotni kā burbuli"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Pēdējās izmantotās lietotnes"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Pēdējo izmantoto lietotņu saraksts"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{papildu lietotne}zero{papildu lietotņu}one{papildu lietotne}other{papildu lietotnes}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Darbvirsma"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"“<xliff:g id="APP_NAME_1">%1$s</xliff:g>” un “<xliff:g id="APP_NAME_2">%2$s</xliff:g>”"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>; <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>. vienums no <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Burbulis"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Pārpilde"</string>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 7362c82ff6..d5fe673309 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести долу десно"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Отвори апликација како балонче"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Неодамнешни апликации"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Список со неодамнешни апликации"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{дополнителна апликација}one{дополнителна апликација}other{дополнителни апликации}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Режим за компјутер"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, ставка <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> од <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Балонче"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Проширено балонче"</string>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 9aa1d1c51d..099e0003e6 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"മുകളിലേക്കോ ഇടത്തേക്കോ നീക്കുക"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"താഴേക്കോ വലത്തേക്കോ നീക്കുക"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"ആപ്പ് ഒരു ബബിളായി തുറക്കുക"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"അടുത്തിടെ തുറന്ന ആപ്പുകൾ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"അടുത്തിടെ തുറന്ന ആപ്പ് ലിസ്റ്റ്"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{കൂടുതൽ ആപ്പ്}other{കൂടുതൽ ആപ്പുകൾ}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ഡെസ്‌ക്ടോപ്പ്"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>-ൽ <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>-ാമത്തെ ഇനം"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ബബിൾ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ഓവർഫ്ലോ"</string>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 129b75c1ec..d41faafd82 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Зүүн дээд хэсэг рүү зөөх"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Баруун доод хэсэг рүү зөөх"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Аппыг бөмбөлөг байдлаар нээх"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Саяхны апп"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Саяхны аппын жагсаалт"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{бусад апп}other{бусад апп}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Дэлгэц"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> болон <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>-н <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>-р зүйл"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Бөмбөлөг"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Илүү хэсэг"</string>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 818b824539..5aa9dfcba7 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सर्वात वरती/डावीकडे हलवा"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"तळाशी/उजवीकडे हलवा"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"बबल म्हणून अ‍ॅप उघडा"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"अलीकडील ॲप्स"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"अलीकडील ॲपची सूची"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{आणखी अ‍ॅप}other{आणखी अ‍ॅप्स}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"डेस्कटॉप"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> आणि <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> पैकी <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> आयटम"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"बबल"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ओव्हरफ्लो"</string>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index c06811beeb..a633bf4a60 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Alihkan ke atas/kiri"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Alihkan ke bawah/kanan"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Buka apl sebagai gelembung"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Apl terbaharu"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Senarai apl terbaharu"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{apl lagi}other{apl lagi}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dan <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> item daripada <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Gelembung"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Limpahan"</string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index b556deddc8..499269c27f 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"အပေါ်/ဘယ်ဘက်သို့ ရွှေ့ရန်"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"အောက်ခြေ/ညာဘက်သို့ ရွှေ့ရန်"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"အက်ပ်ကို ပူဖောင်းကွက်အဖြစ် ဖွင့်ရန်"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"မကြာသေးမီက အက်ပ်များ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"မကြာသေးမီက အက်ပ်စာရင်း"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{နောက်ထပ်အက်ပ်}other{နောက်ထပ်အက်ပ်များ}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ဒက်စ်တော့"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> နှင့် <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>၊ <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> ခုအနက် <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ပူဖောင်းကွက်"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"မီနူးအပို"</string>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 4528a83cc3..7ec515ce98 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytt til øverst/venstre"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytt til nederst/høyre"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Åpne appen som en boble"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Nylige apper"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Liste over nylige apper"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app til}other{apper til}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Skrivebord"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> og <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, element <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> av <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Boble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflyt"</string>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 2d35a223c8..91c6bba275 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"सिरान/बायाँतिर सार्नुहोस्"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"फेद/दायाँतिर सार्नुहोस्"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"एपलाई बबलका रूपमा खोल्नुहोस्"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"हालसालै चलाइएका एपहरू"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"हालसालै चलाइएको एपको सूची"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{थप एप}other{थप एपहरू}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"डेस्कटप"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> र <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> मध्ये <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> औँ वस्तु"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"बबल"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ओभरफ्लो"</string>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 3086c40915..9e49f13252 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Naar boven/links verplaatsen"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Naar beneden/rechts verplaatsen"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"App openen als ballon"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Recente apps"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lijst met recente apps"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{extra app}other{extra apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> en <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, item <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> van <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubbel"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overloop"</string>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 4581f9751e..4721e74cfb 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ଶୀର୍ଷ/ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ନିମ୍ନ/ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"ଏକ ବବଲ ଭାବେ ଆପ ଖୋଲନ୍ତୁ"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"ବର୍ତ୍ତମାନର ଆପ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"ବର୍ତ୍ତମାନର ଆପ ତାଲିକା"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ଅଧିକ ଆପ}other{ଅଧିକ ଆପ୍ସ}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ଡେସ୍କଟପ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ଏବଂ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>ର <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> ଆଇଟମ"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ବବଲ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ଓଭରଫ୍ଲୋ"</string>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index ac88e6fb56..f081b67b0e 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ਸਿਖਰਲੇ/ਖੱਬੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ਹੇਠਾਂ/ਸੱਜੇ ਪਾਸੇ ਲੈ ਕੇ ਜਾਓ"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"ਐਪ ਨੂੰ ਬਬਲ ਵਜੋਂ ਖੋਲ੍ਹੋ"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"ਹਾਲੀਆ ਐਪਾਂ"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"ਹਾਲੀਆ ਐਪ ਸੂਚੀ"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ਹੋਰ ਐਪ}one{ਹੋਰ ਐਪ}other{ਹੋਰ ਐਪਾਂ}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ਡੈਸਕਟਾਪ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ਅਤੇ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> ਵਿੱਚੋਂ <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> ਆਈਟਮ"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ਬਬਲ"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ਓਵਰਫ਼ਲੋ"</string>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 090dc4ccf3..9970dee960 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Przesuń w górny lewy róg"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Przesuń w dolny prawy róg"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otwórz aplikację jako dymek"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Ostatnie aplikacje"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lista ostatnich aplikacji"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{inna aplikacja}few{inne aplikacje}many{innych aplikacji}other{innej aplikacji}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Pulpit"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> i <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, element <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> z <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Dymek"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Rozwijany"</string>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index ff5dbf72d5..7f3a3c2c2e 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para a parte superior esquerda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para a part superior direita"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir app como um balão"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Apps recentes"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lista de apps recentes"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{outra app}other{outras apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Computador"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, item <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> de <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Balão"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Menu adicional"</string>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index bc0b8382a4..92fed63ed5 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mover para cima/para a esquerda"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mover para baixo/para a direita"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Abrir o app como um balão"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Apps recentes"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lista de apps recentes"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{outro app}one{outro app}other{outros apps}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Computador"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> e <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, item <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> de <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Balão"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Balão flutuante"</string>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 0d1aab1a87..4b1ee4d8ff 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Mută în stânga sus"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Mută în dreapta jos"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Deschide aplicația ca balon"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Aplicații recente"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lista de aplicații recente"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplicație suplimentară}few{mai multe aplicații}other{mai multe aplicații}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Computer"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> și <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, articolul <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> din <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Balon"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Suplimentar"</string>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 3cf299d669..f46de34767 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Переместить вверх или влево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Переместить вниз или вправо"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Открыть приложение во всплывающем окне"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Недавние приложения"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Список недавних приложений"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{дополнительное приложение}one{дополнительное приложение}few{дополнительных приложения}many{дополнительных приложений}other{дополнительного приложения}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Режим компьютера"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, элемент <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> из <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Всплывающая подсказка"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Дополнительное меню"</string>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 0b4efc4210..d413b0dd70 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ඉහළ/වම වෙත ගෙන යන්න"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"පහළ/දකුණ වෙත ගෙන යන්න"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"යෙදුම බුබුලක් ලෙස විවෘත කරන්න"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"මෑත යෙදුම්"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"මෑත යෙදුම් ලැයිස්තුව"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{තව යෙදුම}one{තවත් යෙදුම්}other{තවත් යෙදුම්}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ඩෙස්ක්ටොපය"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> සහ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, අයිතම <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>න් <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"බුබුළු"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"පිටාර යාම"</string>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 9ca4ae7d89..8b87d38c27 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Presunúť hore alebo doľava"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Presunúť dole alebo doprava"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Otvoriť aplikáciu ako bublinu"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Nedávne aplikácie"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Zoznam nedávnych aplikácií"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ďalšia aplikácia}few{ďalšie aplikácie}many{ďalšie aplikácie}other{ďalšie aplikácie}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Počítač"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> a <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>. položka z <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bublina"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Rozbaľovacia ponuka"</string>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 9a437bcc59..1f87e650ad 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Premakni na vrh/levo"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Premakni na dno/desno"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Odpri aplikacijo kot oblaček"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Nedavne aplikacije"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Seznam nedavnih aplikacij"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{dodatna aplikacija}one{dodatna aplikacija}two{dodatni aplikaciji}few{dodatne aplikacije}other{dodatnih aplikacij}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Namizni računalnik"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> in <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, element <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> od <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Oblaček"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Oblaček z dodatnimi elementi"</string>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index c72b926ec5..5ab85facaf 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Lëviz në krye/majtas"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Lëviz në fund/djathtas"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Hap aplikacionin si një flluskë"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Aplikacionet e fundit"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lista e aplikacioneve të fundit"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{aplikacion tjetër}other{aplikacione të tjera}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> dhe <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, artikulli <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> nga <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Flluska"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Tejkalimi"</string>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index c418302ad6..4e32155e4c 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Премести горе лево"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Премести доле десно"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Отвори апликацију као облачић"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Недавне апликације"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Листа недавних апликација"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{додатна апликација}one{додатна апликација}few{додатне апликације}other{додатних апликација}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Рачунар"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> и <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, ставка <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> од <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Облачић"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Преклопни"</string>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 76c8d3dbeb..0ac4d4615b 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Flytta högst upp/till vänster"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Flytta längst ned/till höger"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Öppna appen som en bubbla"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Senaste apparna"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Lista över senaste appar"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{app till}other{appar till}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Skrivbordsläge"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> och <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, objekt <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> av <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubbla"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Fler alternativ"</string>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index ca800d9867..b3afda5368 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sogeza juu/kushoto"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sogeza chini/kulia"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Fungua programu kama kiputo"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Programu ulizofungua hivi majuzi"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Orodha ya programu ulizofungua hivi majuzi"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{programu nyingine}other{programu zingine}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Kompyuta ya Mezani"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> na <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, kipengee cha <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> kati ya <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Kiputo"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Kiputo cha vipengee vya ziada"</string>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 236982a037..f6e102124b 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -90,7 +90,7 @@
<string name="allset_title" msgid="5021126669778966707">"அனைத்தையும் அமைத்துவிட்டீர்கள்!"</string>
<string name="allset_hint" msgid="459504134589971527">"முகப்புக்குச் செல்ல மேல்நோக்கி ஸ்வைப் செய்யுங்கள்"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"முகப்புத் திரைக்குச் செல்வதற்கு முகப்பு பட்டனைத் தட்டவும்"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> ஐப் பயன்படுத்தத் தயாராகிவிட்டீர்கள்"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> உங்களுக்காகத் தயாராக இருக்கிறது"</string>
<string name="default_device_name" msgid="6660656727127422487">"சாதனம்"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"சிஸ்டம் வழிசெலுத்தல் அமைப்புகள்"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"பகிர்"</string>
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"மேலே/இடதுபுறம் நகர்த்தும்"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"கீழே/வலதுபுறம் நகர்த்தும்"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"ஆப்ஸைக் குமிழாகத் திற"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"சமீபத்திய ஆப்ஸ்"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"சமீபத்திய ஆப்ஸ் பட்டியல்"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{கூடுதல் ஆப்ஸ்}other{கூடுதல் ஆப்ஸ்}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"டெஸ்க்டாப்"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> மற்றும் <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> இல் <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> கட்டம்"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"குமிழ்"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"கூடுதல் விருப்பங்களைக் காட்டும்"</string>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index d5bc982dba..5ab198e81d 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ఎగువ/ఎడమ వైపునకు తరలించండి"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"దిగువ/కుడి వైపునకు తరలించండి"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"యాప్‌ను బబుల్‌లాగా తెరవండి"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"ఇటీవలి యాప్‌లు"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"ఇటీవలి యాప్ లిస్ట్"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{మరో యాప్‌}other{మరిన్ని యాప్‌లు}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"డెస్క్‌టాప్"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>‌లో <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>‌వ ఐటెమ్"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"బబుల్"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"ఓవర్‌ఫ్లో"</string>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 67673582c5..98380033c2 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"ย้ายไปที่ด้านบนหรือด้านซ้าย"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"ย้ายไปที่ด้านล่างหรือด้านขวา"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"เปิดแอปเป็นบับเบิล"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"แอปล่าสุด"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"รายการแอปล่าสุด"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{แอปเพิ่มเติม}other{แอปเพิ่มเติม}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"เดสก์ท็อป"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> และ <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, รายการที่ <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> จาก <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"บับเบิล"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"การดำเนินการเพิ่มเติม"</string>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 479a5d358f..52619b9880 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Ilipat sa itaas/kaliwa"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Ilipat sa ibaba/kanan"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Buksan ang app bilang bubble"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Mga kamakailang app"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Kamakailang listahan ng app"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{pang app}one{pang app}other{pang app}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Desktop"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> at <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, item <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> ng <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bubble"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Overflow"</string>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index e68056ad0f..f30205a907 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Sol üste taşı"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Sağ alta taşı"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Uygulamayı balon olarak aç"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Son uygulamalar"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Son uygulama listesi"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{uygulama daha}other{uygulama daha}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Masaüstü"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> ve <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>/<xliff:g id="TOTAL_TASKS">%3$d</xliff:g> öğe"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Balon"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Taşma"</string>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 350b23b85c..4cda8cfc79 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Перемістити вгору або вліво"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Перемістити вниз або вправо"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Відкрити додаток у спливаючому вікні"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Нещодавні додатки"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Список нещодавніх додатків"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{інший додаток}one{інший додаток}few{інші додатки}many{інших додатків}other{іншого додатка}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Комп’ютер"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> та <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, об’єкт <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> з <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Повідомлення"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Додаткове повідомлення"</string>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 3b46f51cc5..a5508d47c5 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"اوپر/بائیں طرف منتقل کریں"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"نیچے/دائیں طرف منتقل کریں"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"ایپ کو بطور ببل کھولیں"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"حالیہ ایپس"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"حالیہ ایپ کی فہرست"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{مزید ایپ}other{مزید ایپس}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"ڈیسک ٹاپ"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> اور <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"‫<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، آئٹم <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> از <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"ببل"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"اوورفلو"</string>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 3f616580a6..e091d69a33 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Yuqoriga yoki chapga oʻtkazish"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Pastga yoki oʻngga oʻtkazish"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Ilovani qalqib chiquvchi oynada ochish"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Oxirgi ilovalar"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Oxirgi ilovalar roʻyxati"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{boshqa ilova}other{boshqa ilovalar}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Kompyuter"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> va <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g>-element, jami: <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> ta"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Pufak"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Kengaytirish"</string>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 6f486606b3..44cfb44b93 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Chuyển lên trên cùng/sang bên trái"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Chuyển xuống dưới cùng/sang bên phải"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Mở ứng dụng dưới dạng bong bóng"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Các ứng dụng gần đây"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Danh sách ứng dụng gần đây"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{ứng dụng khác}other{ứng dụng khác}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Máy tính"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g> và <xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, mục <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> trong số <xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Bong bóng"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Bong bóng bổ sung"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 8e8badc88b..0494f54035 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到顶部/左侧"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右侧"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"以气泡的形式打开应用"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"最近打开过的应用"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"“最近打开过的应用”列表"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{多个应用}other{多个应用}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"桌面模式"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"<xliff:g id="APP_NAME_1">%1$s</xliff:g>和<xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>,第 <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> 项(共 <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> 项)"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"气泡框"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"溢出式气泡框"</string>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 9f5745a35d..d199ee1dec 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移至上方/左側"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移至底部/右側"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"在小視窗開啟應用程式"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"最近開啟的應用程式"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"「最近開啟的應用程式」清單"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個其他應用程式}other{個其他應用程式}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"桌面"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」和「<xliff:g id="APP_NAME_2">%2$s</xliff:g>」"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>,第 <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> 個項目,總共有 <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> 項"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"對話氣泡"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"展開式"</string>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index d11cafda29..7b9efb7ef5 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到上方/左側"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右側"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"以泡泡形式開啟應用程式"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"最近開啟的應用程式"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"「最近開啟的應用程式」清單"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{個其他應用程式}other{個其他應用程式}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"電腦模式"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」和「<xliff:g id="APP_NAME_2">%2$s</xliff:g>」"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>,第 <xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> 個項目,共 <xliff:g id="TOTAL_TASKS">%3$d</xliff:g> 項"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"泡泡"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"溢位"</string>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 1d9996599f..8d344e2f7e 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -140,14 +140,15 @@
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"Hamba phezulu/kwesokunxele"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"Hamba phansi/kwesokudla"</string>
<string name="open_app_as_a_bubble" msgid="6642626287247807473">"Vula i-app njengebhamuza"</string>
- <!-- no translation found for quick_switch_pane_title (4677158207760585812) -->
- <skip />
- <!-- no translation found for quick_switch_content_description (2851244536728720005) -->
- <skip />
+ <string name="quick_switch_pane_title" msgid="4677158207760585812">"Ama-app wakamuva"</string>
+ <string name="quick_switch_content_description" msgid="2851244536728720005">"Uhlu lwe-app lwakamuva"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{i-app eyengeziwe}one{ama-app engeziwe}other{ama-app engeziwe}}"</string>
<string name="quick_switch_desktop" msgid="8393802056024499749">"Ideskithophu"</string>
<string name="quick_switch_split_task" msgid="5598194724255333896">"I-<xliff:g id="APP_NAME_1">%1$s</xliff:g> ne-<xliff:g id="APP_NAME_2">%2$s</xliff:g>"</string>
- <!-- no translation found for quick_switch_task_with_position_in_parent (4968670948331508951) -->
+ <string name="quick_switch_task_with_position_in_parent" msgid="4968670948331508951">"I-<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, into engu-<xliff:g id="INDEX_IN_PARENT">%2$d</xliff:g> kwezingu-<xliff:g id="TOTAL_TASKS">%3$d</xliff:g>"</string>
+ <!-- no translation found for quick_switch_scroll_arrow_left (6527033155534184309) -->
+ <skip />
+ <!-- no translation found for quick_switch_scroll_arrow_right (7319779291086916348) -->
<skip />
<string name="bubble_bar_bubble_fallback_description" msgid="7811684548953452009">"Ibhamuza"</string>
<string name="bubble_bar_overflow_description" msgid="8617628132733151708">"Ukugcwala kakhulu"</string>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 1ce28cf995..49cee0fa86 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -23,7 +23,6 @@
<string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
<string name="test_information_handler_class" translatable="false">com.android.quickstep.QuickstepTestInformationHandler</string>
- <string name="widget_holder_factory_class" translatable="false">com.android.launcher3.uioverrides.QuickstepWidgetHolder$QuickstepHolderFactory</string>
<string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
<string name="app_launch_tracker_class" translatable="false">com.android.launcher3.appprediction.PredictionAppTracker</string>
<string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c74390ed58..86d44c978a 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -82,9 +82,9 @@
<dimen name="task_thumbnail_icon_menu_drawable_touch_size">44dp</dimen>
<dimen name="task_thumbnail_icon_menu_elevation">4dp</dimen>
<!-- The size of the task thumbnail header -->
- <dimen name="task_thumbnail_header_padding_top_bottom">6dp</dimen>
- <dimen name="task_thumbnail_header_padding_start_end">12dp</dimen>
- <dimen name="task_thumbnail_header_margin_between_views">8dp</dimen>
+ <dimen name="task_thumbnail_header_height">30dp</dimen>
+ <dimen name="task_thumbnail_header_margin_edge">18dp</dimen>
+ <dimen name="task_thumbnail_header_margin_between_views">9dp</dimen>
<dimen name="task_thumbnail_header_icon_size">18dp</dimen>
<dimen name="task_thumbnail_header_round_corner_radius">16dp</dimen>
@@ -363,6 +363,7 @@
<dimen name="taskbar_home_button_left_margin_kids">48dp</dimen>
<dimen name="taskbar_icon_size_kids">32dp</dimen>
<dimen name="taskbar_all_apps_search_button_translation_x_offset">6dp</dimen>
+ <dimen name="taskbar_all_apps_search_button_translation_x_offset_for_expressive_theme">5.5dp</dimen>
<dimen name="taskbar_contextual_button_suw_margin">64dp</dimen>
<dimen name="taskbar_contextual_button_suw_height">64dp</dimen>
<dimen name="taskbar_back_button_suw_start_margin">48dp</dimen>
@@ -381,6 +382,7 @@
<dimen name="transient_taskbar_key_shadow_distance">10dp</dimen>
<dimen name="transient_taskbar_stashed_height">32dp</dimen>
<dimen name="transient_taskbar_all_apps_button_translation_x_offset">8dp</dimen>
+ <dimen name="transient_taskbar_all_apps_button_translation_x_offset_for_expressive_theme">8dp</dimen>
<dimen name="transient_taskbar_stash_spring_velocity_dp_per_s">400dp</dimen>
<dimen name="taskbar_tooltip_vertical_padding">8dp</dimen>
<dimen name="taskbar_tooltip_horizontal_padding">16dp</dimen>
@@ -459,7 +461,8 @@
<!-- Container size with pointer included: bubblebar_size + bubblebar_pointer_size -->
<dimen name="bubblebar_size_with_pointer">80dp</dimen>
<dimen name="bubblebar_elevation">1dp</dimen>
- <dimen name="bubblebar_drag_elevation">2dp</dimen>
+ <!-- TODO b/396539130: used wmshared value once resources are fixed -->
+ <dimen name="dragged_bubble_elevation">3dp</dimen>
<dimen name="bubblebar_hotseat_adjustment_threshold">90dp</dimen>
<dimen name="bubblebar_bounce_distance">20dp</dimen>
@@ -532,8 +535,8 @@
<dimen name="keyboard_quick_switch_text_button_fade_edge_length">20dp</dimen>
<dimen name="keyboard_quick_switch_scroll_button_width">36dp</dimen>
<dimen name="keyboard_quick_switch_scroll_button_height">56dp</dimen>
- <dimen name="keyboard_quick_switch_scroll_button_horizontal_padding">12dp</dimen>
- <dimen name="keyboard_quick_switch_scroll_button_vertical_padding">32dp</dimen>
+ <dimen name="keyboard_quick_switch_scroll_button_horizontal_padding">6dp</dimen>
+ <dimen name="keyboard_quick_switch_scroll_button_vertical_padding">16dp</dimen>
<dimen name="keyboard_quick_switch_scroll_button_corner_radius">18dp</dimen>
<!-- Digital Wellbeing -->
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 7578bd546f..f263f7efc3 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -345,12 +345,10 @@
<!-- Accessibility label for quick switch tiles that include information about the tile's position in the parent list [CHAR LIMIT=NONE] -->
<string name="quick_switch_task_with_position_in_parent"><xliff:g id="task_description" example="Chrome">%1$s</xliff:g>, item <xliff:g id="index_in_parent" example="1">%2$d</xliff:g> of <xliff:g id="total_tasks" example="5">%3$d</xliff:g></string>
- <!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content left
- TODO(b/397975686): Make these translatable when verified by UX. -->
- <string name="quick_switch_scroll_arrow_left" translatable="false">Scroll left</string>
- <!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content right
- TODO(b/397975686): Make these translatable when verified by UX. -->
- <string name="quick_switch_scroll_arrow_right" translatable="false">Scroll right</string>
+ <!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content left -->
+ <string name="quick_switch_scroll_arrow_left">Scroll left</string>
+ <!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content right -->
+ <string name="quick_switch_scroll_arrow_right">Scroll right</string>
<!-- Strings for bubble bar -->
<!-- Fallback name for a bubble if it does have a title [CHAR_LIMIT=none] -->
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index aae8a56593..548fbefcae 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -40,12 +40,14 @@ import static com.android.app.animation.Interpolators.DECELERATE_1_5;
import static com.android.app.animation.Interpolators.DECELERATE_1_7;
import static com.android.app.animation.Interpolators.EXAGGERATED_EASE;
import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
import static com.android.launcher3.Flags.enableContainerReturnAnimations;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
+import static com.android.launcher3.Flags.syncAppLaunchWithTaskbarStash;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
@@ -66,6 +68,7 @@ import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
+import static com.android.wm.shell.Flags.enableDynamicInsetsForAppLaunch;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -92,6 +95,7 @@ import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Log;
import android.util.Pair;
import android.util.Size;
import android.view.CrossWindowBlurListeners;
@@ -182,6 +186,7 @@ import java.util.Map.Entry;
* Manages the opening and closing app transitions from Launcher
*/
public class QuickstepTransitionManager implements OnDeviceProfileChangeListener {
+ private static final String TAG = "QuickstepTransitionManager";
private static final boolean ENABLE_SHELL_STARTING_SURFACE =
SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
@@ -279,6 +284,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
private final Interpolator mOpeningXInterpolator;
private final Interpolator mOpeningInterpolator;
+ private final SystemUiProxy mSystemUiProxy;
+
public QuickstepTransitionManager(Context context) {
mLauncher = Launcher.cast(Launcher.getLauncher(context));
mDragLayer = mLauncher.getDragLayer();
@@ -293,6 +300,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
mLauncher.addOnDeviceProfileChangeListener(this);
+ mSystemUiProxy = SystemUiProxy.INSTANCE.get(mLauncher);
if (ENABLE_SHELL_STARTING_SURFACE) {
mTaskStartParams = new LinkedHashMap<>(MAX_NUM_TASKS) {
@@ -302,8 +310,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
};
- SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(
- mStartingWindowListener);
+ mSystemUiProxy.setStartingWindowListener(mStartingWindowListener);
}
mOpeningXInterpolator = AnimationUtils.loadInterpolator(context, R.interpolator.app_open_x);
@@ -356,6 +363,20 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
options.setOnAnimationAbortListener(endCallback);
options.setOnAnimationFinishedListener(endCallback);
options.setLaunchCookie(StableViewInfo.toLaunchCookie(itemInfo));
+
+ // Prepare taskbar for animation synchronization. This needs to happen here before any
+ // app transition is created.
+ LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
+ if (syncAppLaunchWithTaskbarStash()
+ && enableScalingRevealHomeAnimation()
+ && taskbarController != null) {
+ taskbarController.setIgnoreInAppFlagForSync(true);
+ mLauncher.addEventCallback(EVENT_DESTROYED, onEndCallback::executeAllAndDestroy);
+ onEndCallback.add(() -> {
+ taskbarController.setIgnoreInAppFlagForSync(false);
+ });
+ }
+
return new ActivityOptionsWrapper(options, onEndCallback);
}
@@ -504,12 +525,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
4 - rotationChange);
}
}
- if (mDeviceProfile.isTaskbarPresentInApps
- && !target.willShowImeOnTarget
- && !isTransientTaskbar(mLauncher)) {
- // Animate to above the taskbar.
- bounds.bottom -= target.contentInsets.bottom;
- }
return bounds;
}
@@ -676,6 +691,13 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
};
}
+ private boolean shouldCropToInset(RemoteAnimationTarget target) {
+ return enableDynamicInsetsForAppLaunch()
+ && mDeviceProfile.isTaskbarPresentInApps
+ && target != null && !target.willShowImeOnTarget
+ && !isTransientTaskbar(mLauncher);
+ }
+
/**
* @return Animator that controls the window of the opening targets from app icons.
*/
@@ -684,8 +706,19 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
RemoteAnimationTarget[] wallpaperTargets,
RemoteAnimationTarget[] nonAppTargets,
boolean launcherClosing) {
+ RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
+ wallpaperTargets, nonAppTargets, MODE_OPENING);
int rotationChange = getRotationChange(appTargets);
Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
+ final int[] bottomInsetPos = new int[]{
+ mSystemUiProxy.getHomeVisibilityState().getNavbarInsetPosition()};
+ final RemoteAnimationTarget target = openingTargets.getFirstAppTarget();
+ final boolean cropToInset = shouldCropToInset(target);
+ if (cropToInset) {
+ // Animate to above the taskbar.
+ windowTargetBounds.bottom = Math.min(bottomInsetPos[0],
+ windowTargetBounds.bottom);
+ }
boolean appTargetsAreTranslucent = areAllTargetsTranslucent(appTargets);
RectF launcherIconBounds = new RectF();
@@ -698,8 +731,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
Rect crop = new Rect();
Matrix matrix = new Matrix();
- RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
- wallpaperTargets, nonAppTargets, MODE_OPENING);
SurfaceTransactionApplier surfaceApplier =
new SurfaceTransactionApplier(floatingView);
openingTargets.addReleaseCheck(surfaceApplier);
@@ -805,6 +836,39 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
@Override
public void onUpdate(float percent, boolean initOnly) {
+ if (cropToInset && bottomInsetPos[0] != mSystemUiProxy.getHomeVisibilityState()
+ .getNavbarInsetPosition()) {
+ final RemoteAnimationTarget target = openingTargets.getFirstAppTarget();
+ bottomInsetPos[0] = mSystemUiProxy.getHomeVisibilityState()
+ .getNavbarInsetPosition();
+ final Rect bounds = target != null
+ ? target.screenSpaceBounds : windowTargetBounds;
+ // Animate to above the taskbar.
+ int bottomLevel = Math.min(bottomInsetPos[0], bounds.bottom);
+ windowTargetBounds.bottom = bottomLevel;
+ final int endHeight = bottomLevel - bounds.top;
+
+ AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(),
+ mDeviceProfile, windowTargetBounds, launcherIconBounds, v,
+ dragLayerBounds[0], dragLayerBounds[1], hasSplashScreen,
+ floatingView.isDifferentFromAppIcon());
+ mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,
+ mOpeningInterpolator);
+ mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd,
+ mOpeningInterpolator);
+ mDy = new FloatProp(0, prop.dY, mOpeningInterpolator);
+ mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,
+ prop.finalAppIconScale, mOpeningInterpolator);
+ float interpolatedPercent = mOpeningInterpolator.getInterpolation(percent);
+ mCropRectHeight.value = Utilities.mapRange(interpolatedPercent,
+ prop.cropHeightStart, prop.cropHeightEnd);
+ mCropRectCenterY.value = Utilities.mapRange(interpolatedPercent,
+ prop.cropCenterYStart, prop.cropCenterYEnd);
+ mDy.value = Utilities.mapRange(interpolatedPercent, 0, prop.dY);
+ mIconScaleToFitScreen.value = Utilities.mapRange(interpolatedPercent,
+ prop.initialAppIconScale, prop.finalAppIconScale);
+ }
+
// Calculate the size of the scaled icon.
float iconWidth = launcherIconBounds.width() * mIconScaleToFitScreen.value;
float iconHeight = launcherIconBounds.height() * mIconScaleToFitScreen.value;
@@ -1207,7 +1271,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mLauncher.removeOnDeviceProfileChangeListener(this);
SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
if (BuildConfig.IS_STUDIO_BUILD && !mRegisteredTaskStackChangeListener.isEmpty()) {
- throw new IllegalStateException("Failed to run onEndCallback created from"
+ Log.e(TAG, "IllegalState: Failed to run onEndCallback created from"
+ " getActivityLaunchOptions()");
}
mRegisteredTaskStackChangeListener.forEach(TaskRestartedDuringLaunchListener::unregister);
@@ -1903,6 +1967,21 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
anim.addListener(mForceInvisibleListener);
}
+ // Syncs the app launch animation and taskbar stash animation (if exists).
+ if (syncAppLaunchWithTaskbarStash() && enableScalingRevealHomeAnimation()) {
+ LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
+ if (taskbarController != null) {
+ taskbarController.setIgnoreInAppFlagForSync(false);
+
+ if (launcherClosing) {
+ Animator taskbar = taskbarController.createAnimToApp();
+ if (taskbar != null) {
+ anim.play(taskbar);
+ }
+ }
+ }
+ }
+
result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
skipFirstFrame);
}
diff --git a/quickstep/src/com/android/launcher3/dagger/Modules.kt b/quickstep/src/com/android/launcher3/dagger/Modules.kt
index 52be4132f2..7671a82869 100644
--- a/quickstep/src/com/android/launcher3/dagger/Modules.kt
+++ b/quickstep/src/com/android/launcher3/dagger/Modules.kt
@@ -16,11 +16,13 @@
package com.android.launcher3.dagger
+import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepWidgetHolderFactory
import com.android.launcher3.uioverrides.SystemApiWrapper
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl
import com.android.launcher3.util.ApiWrapper
import com.android.launcher3.util.PluginManagerWrapper
import com.android.launcher3.util.window.WindowManagerProxy
+import com.android.launcher3.widget.LauncherWidgetHolder.WidgetHolderFactory
import com.android.quickstep.util.GestureExclusionManager
import com.android.quickstep.util.SystemWindowManagerProxy
import dagger.Binds
@@ -40,6 +42,13 @@ abstract class ApiWrapperModule {
}
@Module
+abstract class WidgetModule {
+
+ @Binds
+ abstract fun bindWidgetHolderFactory(factor: QuickstepWidgetHolderFactory): WidgetHolderFactory
+}
+
+@Module
abstract class PluginManagerWrapperModule {
@Binds
abstract fun bindPluginManagerWrapper(impl: PluginManagerWrapperImpl): PluginManagerWrapper
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
index 1438edf8ef..a9e5145530 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
@@ -93,7 +93,7 @@ class DesktopAppLaunchAnimatorHelper(
}
if (trampolineCloseChange != null) {
val trampolineCloseAnimator =
- createTrampolineCloseAnimator(trampolineCloseChange, transaction)
+ createTrampolineCloseAnimator(trampolineCloseChange, transaction, finishCallback)
animatorsList.add(trampolineCloseAnimator)
}
return animatorsList
@@ -112,7 +112,7 @@ class DesktopAppLaunchAnimatorHelper(
private fun getTrampolineCloseChange(info: TransitionInfo): Change? {
if (
info.changes.size < 2 ||
- !DesktopModeFlags.ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX.isTrue
+ !DesktopModeFlags.ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX.isTrue
) {
return null
}
@@ -194,13 +194,22 @@ class DesktopAppLaunchAnimatorHelper(
)
}
- private fun createTrampolineCloseAnimator(change: Change, transaction: Transaction): Animator {
+ private fun createTrampolineCloseAnimator(
+ change: Change,
+ transaction: Transaction,
+ onAnimFinish: (Animator) -> Unit,
+ ): Animator {
return ValueAnimator.ofFloat(1f, 0f).apply {
duration = 100L
interpolator = Interpolators.LINEAR
addUpdateListener { animation ->
transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
}
+ addListener(
+ onEnd = { animation ->
+ onAnimFinish(animation)
+ }
+ )
}
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 4c24d95462..d9808308e8 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -30,6 +30,8 @@ import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.BaseActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
@@ -46,8 +48,8 @@ import java.util.function.Consumer;
*/
public class DepthController extends BaseDepthController implements StateHandler<LauncherState>,
BaseActivity.MultiWindowModeChangedListener {
-
- private final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw;
+ @VisibleForTesting
+ final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw;
private final Consumer<Boolean> mCrossWindowBlurListener = this::setCrossWindowBlursEnabled;
@@ -58,6 +60,10 @@ public class DepthController extends BaseDepthController implements StateHandler
private View.OnAttachStateChangeListener mOnAttachListener;
+ // Ensure {@link mOnDrawListener} is added only once to avoid spamming DragLayer's mRunQueue
+ // via {@link View#post(Runnable)}
+ private boolean mIsOnDrawListenerAdded = false;
+
public DepthController(Launcher l) {
super(l);
}
@@ -65,34 +71,38 @@ public class DepthController extends BaseDepthController implements StateHandler
private void onLauncherDraw() {
View view = mLauncher.getDragLayer();
ViewRootImpl viewRootImpl = view.getViewRootImpl();
- setSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
- view.post(() -> view.getViewTreeObserver().removeOnDrawListener(mOnDrawListener));
+ setBaseSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
+ view.post(this::removeOnDrawListener);
}
private void ensureDependencies() {
- if (mLauncher.getRootView() != null && mOnAttachListener == null) {
- View rootView = mLauncher.getRootView();
- mOnAttachListener = new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View view) {
- UI_HELPER_EXECUTOR.execute(() ->
- CrossWindowBlurListeners.getInstance().addListener(
- mLauncher.getMainExecutor(), mCrossWindowBlurListener));
- mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
-
- // To handle the case where window token is invalid during last setDepth call.
- applyDepthAndBlur();
- }
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- removeSecondaryListeners();
- }
- };
- rootView.addOnAttachStateChangeListener(mOnAttachListener);
- if (rootView.isAttachedToWindow()) {
- mOnAttachListener.onViewAttachedToWindow(rootView);
+ View rootView = mLauncher.getRootView();
+ if (rootView == null) {
+ return;
+ }
+ if (mOnAttachListener != null) {
+ return;
+ }
+ mOnAttachListener = new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ UI_HELPER_EXECUTOR.execute(() ->
+ CrossWindowBlurListeners.getInstance().addListener(
+ mLauncher.getMainExecutor(), mCrossWindowBlurListener));
+ mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
+
+ // To handle the case where window token is invalid during last setDepth call.
+ applyDepthAndBlur();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ removeSecondaryListeners();
}
+ };
+ rootView.addOnAttachStateChangeListener(mOnAttachListener);
+ if (rootView.isAttachedToWindow()) {
+ mOnAttachListener.onViewAttachedToWindow(rootView);
}
}
@@ -109,11 +119,9 @@ public class DepthController extends BaseDepthController implements StateHandler
}
private void removeSecondaryListeners() {
- if (mCrossWindowBlurListener != null) {
- UI_HELPER_EXECUTOR.execute(() ->
- CrossWindowBlurListeners.getInstance()
- .removeListener(mCrossWindowBlurListener));
- }
+ UI_HELPER_EXECUTOR.execute(() ->
+ CrossWindowBlurListeners.getInstance()
+ .removeListener(mCrossWindowBlurListener));
if (mOpaquenessListener != null) {
mLauncher.getScrimView().removeOpaquenessListener(mOpaquenessListener);
}
@@ -124,10 +132,10 @@ public class DepthController extends BaseDepthController implements StateHandler
*/
public void setActivityStarted(boolean isStarted) {
if (isStarted) {
- mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
+ addOnDrawListener();
} else {
- mLauncher.getDragLayer().getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
- setSurface(null);
+ removeOnDrawListener();
+ setBaseSurface(null);
}
}
@@ -139,7 +147,7 @@ public class DepthController extends BaseDepthController implements StateHandler
stateDepth.setValue(toState.getDepth(mLauncher));
if (toState == LauncherState.BACKGROUND_APP) {
- mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
+ addOnDrawListener();
}
}
@@ -165,7 +173,23 @@ public class DepthController extends BaseDepthController implements StateHandler
@Override
protected void onInvalidSurface() {
// Lets wait for surface to become valid again
+ addOnDrawListener();
+ }
+
+ private void addOnDrawListener() {
+ if (mIsOnDrawListenerAdded) {
+ return;
+ }
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
+ mIsOnDrawListenerAdded = true;
+ }
+
+ private void removeOnDrawListener() {
+ if (!mIsOnDrawListenerAdded) {
+ return;
+ }
+ mLauncher.getDragLayer().getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
+ mIsOnDrawListenerAdded = false;
}
@Override
@@ -189,7 +213,8 @@ public class DepthController extends BaseDepthController implements StateHandler
writer.println(prefix + "DepthController");
writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
- writer.println(prefix + "\tmSurface=" + mSurface);
+ writer.println(prefix + "\tmBaseSurface=" + mBaseSurface);
+ writer.println(prefix + "\tmBaseSurfaceOverride=" + mBaseSurfaceOverride);
writer.println(prefix + "\tmStateDepth=" + stateDepth.getValue());
writer.println(prefix + "\tmWidgetDepth=" + widgetDepth.getValue());
writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
index 810fa6f082..1ac2d7c13e 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
@@ -40,7 +40,8 @@ import com.android.quickstep.SystemUiProxy
import com.android.quickstep.fallback.RecentsState
import com.android.wm.shell.desktopmode.DisplayDeskState
import com.android.wm.shell.desktopmode.IDesktopTaskListener.Stub
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.enableMultipleDesktops
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useRoundedCorners
import java.io.PrintWriter
import java.lang.ref.WeakReference
import javax.inject.Inject
@@ -89,8 +90,7 @@ constructor(
private val taskbarDesktopModeListeners: MutableSet<TaskbarDesktopModeListener> = HashSet()
// This simply indicates that user is currently in desktop mode or not.
- var isInDesktopMode = false
- private set
+ @Deprecated("Does not work with multi-desks") private var isInDesktopModeDeprecated = false
// to track if any pending notification to be done.
var isNotifyingDesktopVisibilityPending = false
@@ -104,12 +104,16 @@ constructor(
* Number of visible desktop windows in desktop mode. This can be > 0 when user goes to overview
* from desktop window mode.
*/
- var visibleDesktopTasksCount: Int = 0
+ @Deprecated("Does not work with multi-desks")
+ var visibleDesktopTasksCountDeprecated: Int = 0
/**
* Sets the number of desktop windows that are visible and updates launcher visibility based
* on it.
*/
set(visibleTasksCount) {
+ if (enableMultipleDesktops(context)) {
+ return
+ }
if (DEBUG) {
Log.d(
TAG,
@@ -121,11 +125,11 @@ constructor(
}
if (visibleTasksCount != field) {
- if (visibleDesktopTasksCount == 0 && visibleTasksCount == 1) {
- isInDesktopMode = true
+ if (visibleDesktopTasksCountDeprecated == 0 && visibleTasksCount == 1) {
+ isInDesktopModeDeprecated = true
}
- if (visibleDesktopTasksCount == 1 && visibleTasksCount == 0) {
- isInDesktopMode = false
+ if (visibleDesktopTasksCountDeprecated == 1 && visibleTasksCount == 0) {
+ isInDesktopModeDeprecated = false
}
val wasVisible = field > 0
val isVisible = visibleTasksCount > 0
@@ -171,7 +175,7 @@ constructor(
private var desktopTaskListener: DesktopTaskListenerImpl?
init {
- desktopTaskListener = DesktopTaskListenerImpl(this, context.displayId)
+ desktopTaskListener = DesktopTaskListenerImpl(this, context, context.displayId)
systemUiProxy.setDesktopTaskListener(desktopTaskListener)
lifecycleTracker.addCloseable {
@@ -185,7 +189,7 @@ constructor(
* [INACTIVE_DESK_ID] if no desk is currently active or the multiple desks feature is disabled.
*/
fun getActiveDeskId(displayId: Int): Int {
- if (!DesktopModeStatus.enableMultipleDesktops(context)) {
+ if (!enableMultipleDesktops(context)) {
// When the multiple desks feature is disabled, callers should not rely on the concept
// of a desk ID.
return INACTIVE_DESK_ID
@@ -196,8 +200,8 @@ constructor(
/** Returns whether a desk is currently active on the display with the given [displayId]. */
fun isInDesktopMode(displayId: Int): Boolean {
- if (!DesktopModeStatus.enableMultipleDesktops(context)) {
- return isInDesktopMode
+ if (!enableMultipleDesktops(context)) {
+ return isInDesktopModeDeprecated
}
val activeDeskId = getDisplayDeskConfig(displayId)?.activeDeskId ?: INACTIVE_DESK_ID
@@ -213,7 +217,7 @@ constructor(
* Overview is not active.
*/
fun isInDesktopModeAndNotInOverview(displayId: Int): Boolean {
- if (!DesktopModeStatus.enableMultipleDesktops(context)) {
+ if (!enableMultipleDesktops(context)) {
return areDesktopTasksVisibleAndNotInOverview()
}
@@ -225,7 +229,7 @@ constructor(
/** Whether desktop tasks are visible in desktop mode. */
private fun areDesktopTasksVisibleAndNotInOverview(): Boolean {
- val desktopTasksVisible: Boolean = visibleDesktopTasksCount > 0
+ val desktopTasksVisible: Boolean = visibleDesktopTasksCountDeprecated > 0
if (DEBUG) {
Log.d(
TAG,
@@ -309,7 +313,7 @@ constructor(
inOverviewState = overviewStateEnabled
val areDesktopTasksVisibleNow = areDesktopTasksVisibleAndNotInOverview()
- if (!DesktopModeStatus.enableMultipleDesktops(context)) {
+ if (!enableMultipleDesktops(context)) {
if (wereDesktopTasksVisibleBefore != areDesktopTasksVisibleNow) {
notifyIsInDesktopModeChanged(DEFAULT_DISPLAY, areDesktopTasksVisibleNow)
}
@@ -504,7 +508,7 @@ constructor(
displayDeskStates: Array<DisplayDeskState>,
canCreateDesks: Boolean,
) {
- if (!DesktopModeStatus.enableMultipleDesktops(context)) {
+ if (!enableMultipleDesktops(context)) {
return
}
@@ -527,7 +531,7 @@ constructor(
?: null.also { Slog.e(TAG, "Expected non-null desk config for display: $displayId") }
private fun onCanCreateDesksChanged(canCreateDesks: Boolean) {
- if (!DesktopModeStatus.enableMultipleDesktops(context)) {
+ if (!enableMultipleDesktops(context)) {
return
}
@@ -535,7 +539,7 @@ constructor(
}
private fun onDeskAdded(displayId: Int, deskId: Int) {
- if (!DesktopModeStatus.enableMultipleDesktops(context)) {
+ if (!enableMultipleDesktops(context)) {
return
}
@@ -549,7 +553,7 @@ constructor(
}
private fun onDeskRemoved(displayId: Int, deskId: Int) {
- if (!DesktopModeStatus.enableMultipleDesktops(context)) {
+ if (!enableMultipleDesktops(context)) {
return
}
@@ -566,7 +570,7 @@ constructor(
}
private fun onActiveDeskChanged(displayId: Int, newActiveDesk: Int, oldActiveDesk: Int) {
- if (!DesktopModeStatus.enableMultipleDesktops(context)) {
+ if (!enableMultipleDesktops(context)) {
return
}
@@ -626,7 +630,7 @@ constructor(
pw.println(prefix + "DesktopVisibilityController:")
pw.println("$prefix\tdesktopVisibilityListeners=$desktopVisibilityListeners")
- pw.println("$prefix\tvisibleDesktopTasksCount=$visibleDesktopTasksCount")
+ pw.println("$prefix\tvisibleDesktopTasksCount=$visibleDesktopTasksCountDeprecated")
pw.println("$prefix\tinOverviewState=$inOverviewState")
pw.println("$prefix\tbackgroundStateEnabled=$backgroundStateEnabled")
pw.println("$prefix\tgestureInProgress=$gestureInProgress")
@@ -640,6 +644,7 @@ constructor(
*/
private class DesktopTaskListenerImpl(
controller: DesktopVisibilityController,
+ @ApplicationContext private val context: Context,
private val displayId: Int,
) : Stub() {
private val controller = WeakReference(controller)
@@ -660,7 +665,7 @@ constructor(
if (DEBUG) {
Log.d(TAG, "desktop visible tasks count changed=$visibleTasksCount")
}
- visibleDesktopTasksCount = visibleTasksCount
+ visibleDesktopTasksCountDeprecated = visibleTasksCount
}
}
}
@@ -670,7 +675,7 @@ constructor(
}
override fun onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding: Boolean) {
- if (!DesktopModeStatus.useRoundedCorners()) return
+ if (!useRoundedCorners()) return
MAIN_EXECUTOR.execute {
controller.get()?.apply {
Log.d(
@@ -683,7 +688,10 @@ constructor(
}
}
+ // TODO: b/402496827 - The multi-desks backend needs to be updated to call this API only
+ // once, not between desk switches.
override fun onEnterDesktopModeTransitionStarted(transitionDuration: Int) {
+ val controller = controller.get() ?: return
MAIN_EXECUTOR.execute {
Log.d(
TAG,
@@ -691,15 +699,19 @@ constructor(
"duration= " +
transitionDuration),
)
- val controller = controller.get()
- if (controller != null && !controller.isInDesktopMode) {
- controller.isInDesktopMode = true
+ if (enableMultipleDesktops(context)) {
+ controller.notifyTaskbarDesktopModeListenersForEntry(transitionDuration)
+ } else if (!controller.isInDesktopModeDeprecated) {
+ controller.isInDesktopModeDeprecated = true
controller.notifyTaskbarDesktopModeListenersForEntry(transitionDuration)
}
}
}
+ // TODO: b/402496827 - The multi-desks backend needs to be updated to call this API only
+ // once, not between desk switches.
override fun onExitDesktopModeTransitionStarted(transitionDuration: Int) {
+ val controller = controller.get() ?: return
MAIN_EXECUTOR.execute {
Log.d(
TAG,
@@ -707,9 +719,10 @@ constructor(
"duration= " +
transitionDuration),
)
- val controller = controller.get()
- if (controller != null && controller.isInDesktopMode) {
- controller.isInDesktopMode = false
+ if (enableMultipleDesktops(context)) {
+ controller.notifyTaskbarDesktopModeListenersForExit(transitionDuration)
+ } else if (controller.isInDesktopModeDeprecated) {
+ controller.isInDesktopModeDeprecated = false
controller.notifyTaskbarDesktopModeListenersForExit(transitionDuration)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/BarsLocationAnimatorHelper.kt b/quickstep/src/com/android/launcher3/taskbar/BarsLocationAnimatorHelper.kt
index b8060e1792..ad847b47ee 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BarsLocationAnimatorHelper.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/BarsLocationAnimatorHelper.kt
@@ -20,6 +20,7 @@ import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
+import android.content.Context
import android.view.View
import androidx.dynamicanimation.animation.SpringForce
import com.android.app.animation.Interpolators
@@ -30,11 +31,10 @@ import com.android.wm.shell.shared.bubbles.BubbleBarLocation
/** Animator helper that creates bars animators. */
object BarsLocationAnimatorHelper {
-
- private const val FADE_OUT_ANIM_ALPHA_DURATION_MS: Long = 50L
- private const val FADE_OUT_ANIM_ALPHA_DELAY_MS: Long = 50L
- private const val FADE_OUT_ANIM_POSITION_DURATION_MS: Long = 100L
- private const val FADE_IN_ANIM_ALPHA_DURATION_MS: Long = 100L
+ const val FADE_OUT_ANIM_ALPHA_DURATION_MS: Long = 50L
+ const val FADE_OUT_ANIM_ALPHA_DELAY_MS: Long = 50L
+ const val FADE_OUT_ANIM_POSITION_DURATION_MS: Long = 100L
+ const val FADE_IN_ANIM_ALPHA_DURATION_MS: Long = 100L
// Use STIFFNESS_MEDIUMLOW which is not defined in the API constants
private const val FADE_IN_ANIM_POSITION_SPRING_STIFFNESS: Float = 400f
@@ -45,13 +45,13 @@ object BarsLocationAnimatorHelper {
// During fade in animation we shift the bubble bar 1/60th of the screen width
private const val FADE_IN_ANIM_POSITION_SHIFT: Float = 1 / 60f
- private val View.screenWidth: Int
+ private val Context.screenWidth: Int
get() = resources.displayMetrics.widthPixels
- private val View.outShift: Float
+ val Context.outShift: Float
get() = screenWidth * FADE_OUT_ANIM_POSITION_SHIFT
- private val View.inShiftX: Float
+ val Context.inShiftX: Float
get() = screenWidth * FADE_IN_ANIM_POSITION_SHIFT
/**
@@ -108,7 +108,7 @@ object BarsLocationAnimatorHelper {
targetViewAlphaAnim: ObjectAnimator,
bubbleBarView: View,
): Animator {
- val shift: Float = bubbleBarView.outShift
+ val shift: Float = bubbleBarView.context.outShift
val onLeft = newLocation.isOnLeft(bubbleBarView.isLayoutRtl)
val startTx: Float
@@ -132,15 +132,19 @@ object BarsLocationAnimatorHelper {
return createLocationInAnimator(startTx, finalTx, targetViewAlphaAnim, bubbleBarView)
}
- /** Creates an animator for the bubble bar view out part. */
+ /**
+ * Creates an animator for the bubble bar view out part.
+ *
+ * @param targetLocation the location bubble bar should animate to.
+ */
@JvmStatic
fun getBubbleBarLocationOutAnimator(
bubbleBarView: View,
- bubbleBarLocation: BubbleBarLocation,
+ targetLocation: BubbleBarLocation,
targetViewAlphaAnim: ObjectAnimator,
): Animator {
- val onLeft = bubbleBarLocation.isOnLeft(bubbleBarView.isLayoutRtl)
- val shift = bubbleBarView.outShift
+ val onLeft = targetLocation.isOnLeft(bubbleBarView.isLayoutRtl)
+ val shift = bubbleBarView.context.outShift
val finalTx = bubbleBarView.translationX + (if (onLeft) -shift else shift)
return this.createLocationOutAnimator(finalTx, targetViewAlphaAnim, bubbleBarView)
}
@@ -152,7 +156,7 @@ object BarsLocationAnimatorHelper {
navButtonsView: View,
navBarTargetTranslationX: Float,
): Animator {
- val outShift: Float = navButtonsView.outShift
+ val outShift: Float = navButtonsView.context.outShift
val isNavBarOnRight: Boolean = location.isOnLeft(navButtonsView.isLayoutRtl)
val finalOutTx =
navButtonsView.translationX + (if (isNavBarOnRight) outShift else -outShift)
@@ -162,7 +166,7 @@ object BarsLocationAnimatorHelper {
ObjectAnimator.ofFloat(navButtonsView, LauncherAnimUtils.VIEW_ALPHA, 0f),
navButtonsView,
)
- val inShift: Float = navButtonsView.inShiftX
+ val inShift: Float = navButtonsView.context.inShiftX
val inStartX = navBarTargetTranslationX + (if (isNavBarOnRight) -inShift else inShift)
val fadeIn: Animator =
createLocationInAnimator(
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index aa3feb7614..b82e6edd7c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -23,7 +23,7 @@ import android.view.LayoutInflater;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.BaseContext;
-import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.Themes;
import com.android.quickstep.SystemUiProxy;
@@ -33,21 +33,49 @@ public abstract class BaseTaskbarContext extends BaseContext
implements SystemShortcut.BubbleActivityStarter {
protected final LayoutInflater mLayoutInflater;
- private final boolean mIsPrimaryDisplay;
public BaseTaskbarContext(Context windowContext, boolean isPrimaryDisplay) {
super(windowContext, Themes.getActivityThemeRes(windowContext));
mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
- mIsPrimaryDisplay = isPrimaryDisplay;
}
- public boolean isTransientTaskbar() {
- return DisplayController.isTransientTaskbar(this) && mIsPrimaryDisplay;
- }
+ /**
+ * Returns whether taskbar is transient or persistent. External displays will be persistent.
+ *
+ * @return {@code true} if transient, {@code false} if persistent.
+ */
+ public abstract boolean isTransientTaskbar();
- public boolean isPrimaryDisplay() {
- return mIsPrimaryDisplay;
- }
+ /**
+ * Returns whether the taskbar is pinned in gesture navigation mode.
+ */
+ public abstract boolean isPinnedTaskbar();
+
+ /**
+ * Returns the current navigation mode. External displays will be in THREE_BUTTONS mode.
+ */
+ public abstract NavigationMode getNavigationMode();
+
+ /**
+ * Returns whether the taskbar is in desktop mode.
+ */
+ public abstract boolean isInDesktopMode();
+
+ /**
+ * Returns whether the taskbar is forced to be pinned when home is visible.
+ */
+ public abstract boolean showLockedTaskbarOnHome();
+
+ /**
+ * Returns whether desktop taskbar (pinned taskbar that shows desktop tasks) is to be used on
+ * the display because the display is a freeform display.
+ */
+ public abstract boolean showDesktopTaskbarForFreeformDisplay();
+
+ /**
+ * Returns whether the taskbar is displayed on primary or external display.
+ */
+ public abstract boolean isPrimaryDisplay();
@Override
public final LayoutInflater getLayoutInflater() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index d6327bc3cd..5b1e8593a1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -26,11 +26,15 @@ import androidx.annotation.Nullable;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StatefulContainer;
+import com.android.quickstep.FallbackActivityInterface;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
+import java.io.PrintWriter;
import java.util.stream.Stream;
/**
@@ -92,11 +96,19 @@ public class FallbackTaskbarUIController
mRecentsContainer.getStateManager().removeStateListener(mStateListener);
}
+ @Nullable
+ @Override
+ public Animator getParallelAnimationToGestureEndTarget(GestureState.GestureEndTarget endTarget,
+ long duration, RecentsAnimationCallbacks callbacks) {
+ return createAnimToRecentsState(
+ FallbackActivityInterface.INSTANCE.stateFromGestureEndTarget(endTarget), duration);
+ }
+
/**
* Creates an animation to animate the taskbar for the given state (but does not start it).
* Currently this animation just force stashes the taskbar in Overview.
*/
- public Animator createAnimToRecentsState(RecentsState toState, long duration) {
+ private Animator createAnimToRecentsState(RecentsState toState, long duration) {
// Force stash taskbar (disallow unstashing) when:
// - in a 3P launcher or overview task.
// - not running in a test harness (unstash is needed for tests)
@@ -134,7 +146,8 @@ public class FallbackTaskbarUIController
private boolean isIn3pHomeOrRecents() {
TopTaskTracker.CachedTaskInfo topTask = TopTaskTracker.INSTANCE
- .get(mControllers.taskbarActivityContext).getCachedTopTask(true);
+ .get(mControllers.taskbarActivityContext).getCachedTopTask(true,
+ mRecentsContainer.asContext().getDisplayId());
return topTask.isHomeTask() || topTask.isRecentsTask();
}
@@ -142,4 +155,12 @@ public class FallbackTaskbarUIController
protected String getTaskbarUIControllerName() {
return "FallbackTaskbarUIController<" + mRecentsContainer.getClass().getSimpleName() + ">";
}
+
+ @Override
+ protected void dumpLogs(String prefix, PrintWriter pw) {
+ super.dumpLogs(prefix, pw);
+
+ pw.println(String.format("%s\tRecentsState=%s", prefix,
+ mRecentsContainer.getStateManager().getState()));
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index ab147bbcd9..515cfe2af0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -47,7 +47,6 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.app.animation.Interpolators;
import com.android.internal.jank.Cuj;
-import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
@@ -180,17 +179,6 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
mIsRtl = Utilities.isRtl(resources);
- if (Flags.taskbarOverflow()) {
- initializeScrollArrows();
-
- if (mIsRtl) {
- mStartScrollArrow.setContentDescription(
- resources.getString(R.string.quick_switch_scroll_arrow_right));
- mEndScrollArrow.setContentDescription(
- resources.getString(R.string.quick_switch_scroll_arrow_left));
- }
- }
-
TypefaceUtils.setTypeface(
mNoRecentItemsPane.findViewById(R.id.no_recent_items_text),
FontFamily.GSF_LABEL_LARGE);
@@ -359,9 +347,21 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
});
}
- private void initializeScrollArrows() {
+
+ void enableScrollArrowSupport() {
+ if (mSupportsScrollArrows) {
+ return;
+ }
mSupportsScrollArrows = true;
+ if (mIsRtl) {
+ mStartScrollArrow.setContentDescription(
+ getResources().getString(R.string.quick_switch_scroll_arrow_right));
+ mEndScrollArrow.setContentDescription(
+ getResources().getString(R.string.quick_switch_scroll_arrow_left));
+ }
+
+
mStartScrollArrow.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index b5f2532482..a312d5f70b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -34,6 +34,7 @@ import androidx.annotation.Nullable;
import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
@@ -119,6 +120,10 @@ public class KeyboardQuickSwitchViewController {
mWasDesktopTaskFilteredOut = wasDesktopTaskFilteredOut;
mWasOpenedFromTaskbar = wasOpenedFromTaskbar;
+ if (Flags.taskbarOverflow() && wasOpenedFromTaskbar) {
+ mKeyboardQuickSwitchView.enableScrollArrowSupport();
+ }
+
mKeyboardQuickSwitchView.applyLoadPlan(
mOverlayContext,
tasks,
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index e998388f33..3d5e214391 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -17,12 +17,14 @@ package com.android.launcher3.taskbar;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
+import static com.android.launcher3.Flags.syncAppLaunchWithTaskbarStash;
import static com.android.launcher3.QuickstepTransitionManager.TASKBAR_TO_APP_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.TRANSIENT_TASKBAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.getTaskbarToHomeDuration;
import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IGNORE_IN_APP;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -46,11 +48,16 @@ import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.OnboardingPrefs;
+import com.android.quickstep.GestureState;
import com.android.quickstep.HomeVisibilityState;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.SplitTask;
import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.RecentsViewContainer;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -98,6 +105,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
// Initialized in init.
private final TaskbarLauncherStateController
mTaskbarLauncherStateController = new TaskbarLauncherStateController();
+ // When overview-in-a-window is enabled, that window is the container, else it is mLauncher.
+ private RecentsViewContainer mRecentsViewContainer;
public LauncherTaskbarUIController(QuickstepLauncher launcher) {
mLauncher = launcher;
@@ -110,8 +119,19 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
mTaskbarLauncherStateController.init(mControllers, mLauncher,
mControllers.getSharedState().sysuiStateFlags);
-
+ final TaskbarActivityContext taskbarContext = mControllers.taskbarActivityContext;
+ if (RecentsWindowFlags.getEnableOverviewInWindow()) {
+ mRecentsViewContainer = RecentsDisplayModel.getINSTANCE()
+ .get(taskbarContext).getRecentsWindowManager(taskbarContext.getDisplayId());
+ }
+ if (mRecentsViewContainer == null) {
+ mRecentsViewContainer = mLauncher;
+ }
mLauncher.setTaskbarUIController(this);
+ if (mRecentsViewContainer != mLauncher) {
+ mRecentsViewContainer.setTaskbarUIController(this);
+ }
+
mHomeState.addListener(mVisibilityChangeListener);
onLauncherVisibilityChanged(
Flags.useActivityOverlay()
@@ -140,6 +160,9 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
mTaskbarLauncherStateController.onDestroy();
mLauncher.setTaskbarUIController(null);
+ if (mRecentsViewContainer != mLauncher) {
+ mRecentsViewContainer.setTaskbarUIController(null);
+ }
mHomeState.removeListener(mVisibilityChangeListener);
}
@@ -201,13 +224,18 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
*/
@Override
public void onLauncherVisibilityChanged(boolean isVisible) {
- if (DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(mLauncher)) {
+ if (DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(mLauncher)
+ && mControllers.taskbarActivityContext.isPrimaryDisplay()) {
DisplayController.INSTANCE.get(mLauncher).notifyConfigChange();
}
+
onLauncherVisibilityChanged(isVisible, false /* fromInit */);
}
private void onLauncherVisibilityChanged(boolean isVisible, boolean fromInitOrDestroy) {
+ if (mControllers == null) {
+ return;
+ }
onLauncherVisibilityChanged(
isVisible,
fromInitOrDestroy,
@@ -221,11 +249,12 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
if (!Flags.predictiveBackToHomePolish()) {
shouldOverrideToFastAnimation |= mLauncher.getPredictiveBackToHomeInProgress();
}
- boolean isPinnedTaskbar = DisplayController.isPinnedTaskbar(mLauncher);
- if (isVisible || isPinnedTaskbar) {
- return getTaskbarToHomeDuration(shouldOverrideToFastAnimation, isPinnedTaskbar);
+
+ boolean isPinned = mControllers.taskbarActivityContext.isPinnedTaskbar();
+ if (isVisible || isPinned) {
+ return getTaskbarToHomeDuration(shouldOverrideToFastAnimation, isPinned);
} else {
- return mControllers.taskbarActivityContext.isTransientTaskbar()
+ return (mControllers.taskbarActivityContext.isTransientTaskbar())
? TRANSIENT_TASKBAR_TRANSITION_DURATION : TASKBAR_TO_APP_DURATION;
}
}
@@ -236,7 +265,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
// Launcher is resumed during the swipe-to-overview gesture under shell-transitions, so
// avoid updating taskbar state in that situation (when it's non-interactive -- or
// "background") to avoid premature animations.
- LauncherState state = mLauncher.getStateManager().getState();
+ LauncherState state = mTaskbarLauncherStateController.getLauncherState();
boolean nonInteractiveState = state.hasFlag(FLAG_NON_INTERACTIVE)
&& !state.isTaskbarAlignedWithHotseat(mLauncher);
if (isVisible && (nonInteractiveState || mSkipLauncherVisibilityChange)) {
@@ -292,13 +321,39 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
/**
* Create Taskbar animation when going from an app to Launcher as part of recents transition.
- * @param toState If known, the state we will end up in when reaching Launcher.
- * @param callbacks callbacks to track the recents animation lifecycle. The state change is
- * automatically reset once the recents animation finishes
+ * {@inheritDoc}
*/
- public Animator createAnimToLauncher(@NonNull LauncherState toState,
- @NonNull RecentsAnimationCallbacks callbacks, long duration) {
- return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
+ @Override
+ public Animator getParallelAnimationToGestureEndTarget(
+ GestureState.GestureEndTarget gestureEndTarget, long duration,
+ RecentsAnimationCallbacks callbacks) {
+ return mTaskbarLauncherStateController.createAnimToLauncher(
+ LauncherActivityInterface.INSTANCE.stateFromGestureEndTarget(gestureEndTarget),
+ callbacks,
+ duration);
+ }
+
+ /**
+ * Create Taskbar animation to be played alongside the Launcher app launch animation.
+ */
+ public @Nullable Animator createAnimToApp() {
+ if (!syncAppLaunchWithTaskbarStash()) {
+ return null;
+ }
+ TaskbarStashController stashController = mControllers.taskbarStashController;
+ stashController.updateStateForFlag(TaskbarStashController.FLAG_IN_APP, true);
+ return stashController.createApplyStateAnimator(stashController.getStashDuration());
+ }
+
+ /**
+ * Temporarily ignore FLAG_IN_APP for app launches to prevent premature taskbar stashing.
+ * This is needed because taskbar gets a signal to stash before we actually start the
+ * app launch animation.
+ */
+ public void setIgnoreInAppFlagForSync(boolean enabled) {
+ if (syncAppLaunchWithTaskbarStash()) {
+ mControllers.taskbarStashController.updateStateForFlag(FLAG_IGNORE_IN_APP, enabled);
+ }
}
public void updateTaskbarLauncherStateGoingHome() {
@@ -482,7 +537,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
@Override
public RecentsView getRecentsView() {
- return mLauncher.getOverviewPanel();
+ return mRecentsViewContainer.getOverviewPanel();
}
@Override
@@ -511,6 +566,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
"WIDGETS_PAGE_PROGRESS_INDEX",
"SYSUI_SURFACE_PROGRESS_INDEX",
"LAUNCHER_PAUSE_PROGRESS_INDEX");
+ pw.println(String.format("%s\tmRecentsWindowContainer=%s", prefix, mRecentsViewContainer));
mTaskbarLauncherStateController.dumpLogs(prefix + "\t", pw);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 33cd75966a..cf3b95274a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.taskbar;
-import static android.view.KeyEvent.ACTION_UP;
import static android.view.View.AccessibilityDelegate;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -73,6 +72,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.graphics.drawable.RotateDrawable;
import android.inputmethodservice.InputMethodService;
+import android.os.Bundle;
import android.os.Handler;
import android.os.SystemProperties;
import android.util.Property;
@@ -85,6 +85,7 @@ import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.Flags;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -121,6 +122,7 @@ import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.StringJoiner;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.IntPredicate;
/**
@@ -174,6 +176,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
private static final int NUM_ALPHA_CHANNELS = 3;
private static final long AUTODIM_TIMEOUT_MS = 2250;
+ private static final long PREDICTIVE_BACK_TIMEOUT_MS = 200;
private final ArrayList<StatePropertyHolder> mPropertyHolders = new ArrayList<>();
private final ArrayList<ImageView> mAllButtons = new ArrayList<>();
@@ -681,12 +684,20 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
/**
* Sets the AccessibilityDelegate for the back button.
+ *
+ * When setting a back button accessibility delegate, make sure to not dispatch any duplicate
+ * click events. Click events get injected in the internal accessibility delegate in
+ * {@link #setupBackButtonAccessibility(View, AccessibilityDelegate)}.
*/
public void setBackButtonAccessibilityDelegate(AccessibilityDelegate accessibilityDelegate) {
if (mBackButton == null) {
return;
}
- mBackButton.setAccessibilityDelegate(accessibilityDelegate);
+ if (predictiveBackThreeButtonNav()) {
+ setupBackButtonAccessibility(mBackButton, accessibilityDelegate);
+ } else {
+ mBackButton.setAccessibilityDelegate(accessibilityDelegate);
+ }
}
public void setWallpaperVisible(boolean isVisible) {
@@ -878,6 +889,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
navButtonController.getButtonContentDescription(buttonType)));
if (predictiveBackThreeButtonNav() && buttonType == BUTTON_BACK) {
// set up special touch listener for back button to support predictive back
+ setupBackButtonAccessibility(buttonView, null);
setBackButtonTouchListener(buttonView, navButtonController);
// Set this View clickable, so that NearestTouchFrame.java forwards closeby touches to
// this View
@@ -891,26 +903,61 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
return buttonView;
}
+ private void setupBackButtonAccessibility(View backButton,
+ AccessibilityDelegate accessibilityDelegate) {
+ View.AccessibilityDelegate backButtonAccessibilityDelegate =
+ new View.AccessibilityDelegate() {
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (accessibilityDelegate != null) {
+ accessibilityDelegate.performAccessibilityAction(host, action, args);
+ }
+ if (action == AccessibilityNodeInfo.ACTION_CLICK) {
+ mControllers.navButtonController.sendBackKeyEvent(KeyEvent.ACTION_DOWN,
+ /*cancelled*/ false);
+ mControllers.navButtonController.sendBackKeyEvent(KeyEvent.ACTION_UP,
+ /*cancelled*/ false);
+ return true;
+ }
+ return super.performAccessibilityAction(host, action, args);
+ }
+ };
+ backButton.setAccessibilityDelegate(backButtonAccessibilityDelegate);
+ }
+
private void setBackButtonTouchListener(View buttonView,
TaskbarNavButtonController navButtonController) {
final RectF rect = new RectF();
+ final AtomicBoolean hasSentDownEvent = new AtomicBoolean(false);
+ final Runnable longPressTimeout = () -> {
+ navButtonController.sendBackKeyEvent(KeyEvent.ACTION_DOWN, /*cancelled*/ false);
+ hasSentDownEvent.set(true);
+ };
buttonView.setOnTouchListener((v, event) -> {
int motionEventAction = event.getAction();
if (motionEventAction == MotionEvent.ACTION_DOWN) {
+ hasSentDownEvent.set(false);
+ mHandler.postDelayed(longPressTimeout, PREDICTIVE_BACK_TIMEOUT_MS);
rect.set(0, 0, v.getWidth(), v.getHeight());
}
boolean isCancelled = motionEventAction == MotionEvent.ACTION_CANCEL
|| (!rect.contains(event.getX(), event.getY())
&& (motionEventAction == MotionEvent.ACTION_MOVE
|| motionEventAction == MotionEvent.ACTION_UP));
- if (motionEventAction != MotionEvent.ACTION_DOWN
- && motionEventAction != MotionEvent.ACTION_UP && !isCancelled) {
- // return early. we don't care about any other cases than DOWN, UP and CANCEL
+ if (motionEventAction != MotionEvent.ACTION_UP && !isCancelled) {
+ // return early. we don't care about any other cases than UP or CANCEL from here on
return false;
}
- int keyEventAction = motionEventAction == MotionEvent.ACTION_DOWN
- ? KeyEvent.ACTION_DOWN : ACTION_UP;
- navButtonController.sendBackKeyEvent(keyEventAction, isCancelled);
+ mHandler.removeCallbacks(longPressTimeout);
+ if (!hasSentDownEvent.get()) {
+ if (isCancelled) {
+ // if it is cancelled and ACTION_DOWN has not been sent yet, return early and
+ // don't send anything to sysui.
+ return false;
+ }
+ navButtonController.sendBackKeyEvent(KeyEvent.ACTION_DOWN, isCancelled);
+ }
+ navButtonController.sendBackKeyEvent(KeyEvent.ACTION_UP, isCancelled);
if (motionEventAction == MotionEvent.ACTION_UP && !isCancelled) {
buttonView.performClick();
buttonView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index de91c548a7..fc93d4a0ab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -65,6 +65,7 @@ import android.content.pm.LauncherApps;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.os.IRemoteCallback;
import android.os.Process;
@@ -98,6 +99,7 @@ import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -107,6 +109,8 @@ import com.android.launcher3.desktop.DesktopAppLaunchTransition;
import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.AppInfo;
@@ -139,7 +143,9 @@ import com.android.launcher3.taskbar.bubbles.stashing.PersistentBubbleStashContr
import com.android.launcher3.taskbar.bubbles.stashing.TransientBubbleStashController;
import com.android.launcher3.taskbar.customization.TaskbarFeatureEvaluator;
import com.android.launcher3.taskbar.customization.TaskbarSpecsEvaluator;
+import com.android.launcher3.taskbar.growth.NudgeController;
import com.android.launcher3.taskbar.navbutton.NearestTouchFrame;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -160,6 +166,7 @@ import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.NavHandle;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
@@ -170,6 +177,7 @@ import com.android.quickstep.util.SplitTask;
import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.animation.ViewRootSync;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
@@ -198,7 +206,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
private static final String WINDOW_TITLE = "Taskbar";
- private static final DesktopModeFlag ENABLE_TASKBAR_BEHIND_SHADE = new DesktopModeFlag(
+ protected static final DesktopModeFlag ENABLE_TASKBAR_BEHIND_SHADE = new DesktopModeFlag(
Flags::enableTaskbarBehindShade, false);
private final @Nullable Context mNavigationBarPanelContext;
@@ -251,6 +259,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
private TaskbarSpecsEvaluator mTaskbarSpecsEvaluator;
+ // Snapshot is used to temporarily draw taskbar behind the shade.
+ private @Nullable View mTaskbarSnapshotView;
+ private @Nullable TaskbarOverlayContext mTaskbarSnapshotOverlay;
+
public TaskbarActivityContext(Context windowContext,
@Nullable Context navigationBarPanelContext, DeviceProfile launcherDp,
TaskbarNavButtonController buttonController,
@@ -308,6 +320,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
if (BubbleBarController.isBubbleBarEnabled()
&& deviceBubbleBarEnabled
&& bubbleBarView != null
+ && isPrimaryDisplay
) {
Optional<BubbleStashedHandleViewController> bubbleHandleController = Optional.empty();
Optional<BubbleBarSwipeController> bubbleBarSwipeController = Optional.empty();
@@ -381,7 +394,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
new TaskbarPinningController(this),
bubbleControllersOptional,
new TaskbarDesktopModeController(this,
- DesktopVisibilityController.INSTANCE.get(this)));
+ DesktopVisibilityController.INSTANCE.get(this)),
+ new NudgeController(this));
mLauncherPrefs = LauncherPrefs.get(this);
onViewCreated();
@@ -403,9 +417,41 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
dispatchDeviceProfileChanged();
}
- /** Returns whether current taskbar is transient. */
+ @Override
public boolean isTransientTaskbar() {
- return super.isTransientTaskbar() && !isPhoneMode();
+ return DisplayController.isTransientTaskbar(this) && mIsPrimaryDisplay && !isPhoneMode();
+ }
+
+ @Override
+ public boolean isPinnedTaskbar() {
+ return DisplayController.isPinnedTaskbar(this);
+ }
+
+ @Override
+ public NavigationMode getNavigationMode() {
+ return isPrimaryDisplay() ? DisplayController.getNavigationMode(this)
+ : NavigationMode.THREE_BUTTONS;
+ }
+
+ @Override
+ public boolean isInDesktopMode() {
+ return mControllers != null
+ && mControllers.taskbarDesktopModeController.isInDesktopMode(getDisplayId());
+ }
+
+ @Override
+ public boolean showLockedTaskbarOnHome() {
+ return DisplayController.showLockedTaskbarOnHome(this);
+ }
+
+ @Override
+ public boolean showDesktopTaskbarForFreeformDisplay() {
+ return DisplayController.showDesktopTaskbarForFreeformDisplay(this);
+ }
+
+ @Override
+ public boolean isPrimaryDisplay() {
+ return mIsPrimaryDisplay;
}
/**
@@ -441,9 +487,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
.setIsTransientTaskbar(true)
.build();
}
- mNavMode = (DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
- && !mIsPrimaryDisplay) ? NavigationMode.THREE_BUTTONS
- : DisplayController.getNavigationMode(this);
+ mNavMode = getNavigationMode();
}
/** Called when the visibility of the bubble bar changed. */
@@ -652,8 +696,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
*/
public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type, String title) {
int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_SLIPPERY
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+ | WindowManager.LayoutParams.FLAG_SLIPPERY;
boolean watchOutside = isTransientTaskbar() || isThreeButtonNav();
if (watchOutside && !isRunningInTestHarness()) {
windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
@@ -994,6 +1037,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
mWindowManager.removeViewImmediate(mDragLayer);
mAddedWindow = false;
}
+ mTaskbarSnapshotView = null;
+ mTaskbarSnapshotOverlay = null;
}
public boolean isDestroyed() {
@@ -1065,6 +1110,80 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
if (skipAnim) {
anim.end();
}
+
+ updateTaskbarSnapshot(anim, isExpanded);
+ }
+
+ private void updateTaskbarSnapshot(AnimatorSet anim, boolean isExpanded) {
+ if (!ENABLE_TASKBAR_BEHIND_SHADE.isTrue()) {
+ return;
+ }
+ if (mTaskbarSnapshotView == null) {
+ mTaskbarSnapshotView = new View(this);
+ }
+ if (isExpanded) {
+ if (!mTaskbarSnapshotView.isAttachedToWindow()
+ && mDragLayer.isAttachedToWindow()
+ && mDragLayer.isLaidOut()
+ && mTaskbarSnapshotView.getParent() == null) {
+ NearestTouchFrame navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
+ int oldNavButtonsVisibility = navButtonsView.getVisibility();
+ navButtonsView.setVisibility(View.INVISIBLE);
+
+ Drawable drawable = new FastBitmapDrawable(BitmapRenderer.createHardwareBitmap(
+ mDragLayer.getWidth(),
+ mDragLayer.getHeight(),
+ mDragLayer::draw));
+
+ navButtonsView.setVisibility(oldNavButtonsVisibility);
+ mTaskbarSnapshotView.setBackground(drawable);
+ mTaskbarSnapshotView.setAlpha(0f);
+
+ mTaskbarSnapshotView.addOnAttachStateChangeListener(
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(@NonNull View v) {
+ mTaskbarSnapshotView.removeOnAttachStateChangeListener(this);
+ anim.end();
+ mTaskbarSnapshotView.setAlpha(1f);
+ if (!Utilities.isRunningInTestHarness()) {
+ ViewRootSync.synchronizeNextDraw(mDragLayer,
+ mTaskbarSnapshotView,
+ () -> {});
+ }
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(@NonNull View v) {}
+ });
+ BaseDragLayer.LayoutParams layoutParams = new BaseDragLayer.LayoutParams(
+ mDragLayer.getWidth(), mDragLayer.getHeight());
+ layoutParams.gravity = mWindowLayoutParams.gravity;
+ layoutParams.ignoreInsets = true;
+ mTaskbarSnapshotOverlay = mControllers.taskbarOverlayController.requestWindow();
+ mTaskbarSnapshotOverlay.getDragLayer().addView(mTaskbarSnapshotView, layoutParams);
+ }
+ } else {
+ Runnable removeSnapshotView = () -> {
+ if (mTaskbarSnapshotOverlay != null) {
+ mTaskbarSnapshotOverlay.getDragLayer().removeView(mTaskbarSnapshotView);
+ mTaskbarSnapshotView = null;
+ mTaskbarSnapshotOverlay = null;
+ }
+ };
+ if (mTaskbarSnapshotView.isAttachedToWindow()) {
+ mTaskbarSnapshotView.setAlpha(0f);
+ anim.end();
+ if (Utilities.isRunningInTestHarness()) {
+ removeSnapshotView.run();
+ } else {
+ ViewRootSync.synchronizeNextDraw(mDragLayer, mTaskbarSnapshotView,
+ removeSnapshotView);
+ }
+ } else {
+ removeSnapshotView.run();
+ }
+ }
}
public void onRotationProposal(int rotation, boolean isValid) {
@@ -1336,11 +1455,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
mControllers.uiController.startSplitSelection(splitSelectSource);
}
- boolean isInDesktopMode() {
- return mControllers != null
- && mControllers.taskbarDesktopModeController.isInDesktopMode(getDisplayId());
- }
-
protected void onTaskbarIconClicked(View view) {
TaskbarUIController taskbarUIController = mControllers.uiController;
RecentsView recents = taskbarUIController.getRecentsView();
@@ -1479,8 +1593,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
} else if (tag instanceof AppInfo) {
// Tapping an item in AllApps
AppInfo info = (AppInfo) tag;
- if (recents != null
- && taskbarUIController.getRecentsView().isSplitSelectionActive()) {
+ if (recents != null && recents.isSplitSelectionActive()) {
// If we are selecting a second app for split, launch the split tasks
taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
} else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index a2b642353a..9e15a60ed1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -25,6 +25,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
+import com.android.launcher3.taskbar.growth.NudgeController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
@@ -67,6 +68,7 @@ public class TaskbarControllers {
public final TaskbarPinningController taskbarPinningController;
public final Optional<BubbleControllers> bubbleControllers;
public final TaskbarDesktopModeController taskbarDesktopModeController;
+ public final NudgeController nudgeController;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
@Nullable private BackgroundRendererController[] mBackgroundRendererControllers = null;
@@ -115,7 +117,8 @@ public class TaskbarControllers {
KeyboardQuickSwitchController keyboardQuickSwitchController,
TaskbarPinningController taskbarPinningController,
Optional<BubbleControllers> bubbleControllers,
- TaskbarDesktopModeController taskbarDesktopModeController) {
+ TaskbarDesktopModeController taskbarDesktopModeController,
+ NudgeController nudgeController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@@ -143,6 +146,7 @@ public class TaskbarControllers {
this.taskbarPinningController = taskbarPinningController;
this.bubbleControllers = bubbleControllers;
this.taskbarDesktopModeController = taskbarDesktopModeController;
+ this.nudgeController = nudgeController;
}
/**
@@ -179,6 +183,7 @@ public class TaskbarControllers {
keyboardQuickSwitchController.init(this);
taskbarPinningController.init(this, mSharedState);
taskbarDesktopModeController.init(this, mSharedState);
+ nudgeController.init(this);
mControllersToLog = new LoggableTaskbarController[] {
taskbarDragController, navButtonController, navbarButtonsViewController,
@@ -189,6 +194,7 @@ public class TaskbarControllers {
voiceInteractionWindowController, taskbarRecentAppsController,
taskbarTranslationController, taskbarEduTooltipController,
keyboardQuickSwitchController, taskbarPinningController,
+ nudgeController
};
mBackgroundRendererControllers = new BackgroundRendererController[] {
taskbarDragLayerController, taskbarScrimViewController,
@@ -344,7 +350,7 @@ public class TaskbarControllers {
return taskbarActivityContext;
}
- protected interface LoggableTaskbarController {
+ public interface LoggableTaskbarController {
void dumpLogs(String prefix, PrintWriter pw);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
index ca8e4cad9f..e96e67dfbc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDesktopModeController.kt
@@ -20,7 +20,6 @@ import android.content.Context
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.statehandlers.DesktopVisibilityController.TaskbarDesktopModeListener
import com.android.launcher3.taskbar.TaskbarBackgroundRenderer.Companion.MAX_ROUNDNESS
-import com.android.launcher3.util.DisplayController
/** Handles Taskbar in Desktop Windowing mode. */
class TaskbarDesktopModeController(
@@ -30,9 +29,6 @@ class TaskbarDesktopModeController(
private lateinit var taskbarControllers: TaskbarControllers
private lateinit var taskbarSharedState: TaskbarSharedState
- val isInDesktopMode: Boolean
- get() = desktopVisibilityController.isInDesktopMode
-
fun init(controllers: TaskbarControllers, sharedState: TaskbarSharedState) {
taskbarControllers = controllers
taskbarSharedState = sharedState
@@ -52,9 +48,10 @@ class TaskbarDesktopModeController(
}
fun shouldShowDesktopTasksInTaskbar(): Boolean {
+ val activityContext = taskbarControllers.taskbarActivityContext
return isInDesktopMode(context.displayId) ||
- DisplayController.showDesktopTaskbarForFreeformDisplay(context) ||
- (DisplayController.showLockedTaskbarOnHome(context) &&
+ activityContext.showDesktopTaskbarForFreeformDisplay() ||
+ (activityContext.showLockedTaskbarOnHome() &&
taskbarControllers.taskbarStashController.isOnHome)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 142f458b3b..4b977e0d23 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -510,6 +510,8 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
} else {
// This will take care of calling maybeOnDragEnd() after the animation
animateGlobalDragViewToOriginalPosition(btv, dragEvent);
+ //TODO(b/399678274): hide drop target in shell
+ notifyBubbleBarItemDragCanceled();
}
mActivity.getDragLayer().setOnDragListener(null);
@@ -536,10 +538,10 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
mControllers.taskbarAutohideSuspendController.updateFlag(
TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, false);
mActivity.onDragEnd();
+ // If an item is dropped on the bubble bar, the bubble bar handles the drop,
+ // so it should not collapse along with the taskbar.
+ boolean droppedOnBubbleBar = notifyBubbleBarItemDropped();
if (mReturnAnimator == null) {
- // If an item is dropped on the bubble bar, the bubble bar handles the drop,
- // so it should not collapse along with the taskbar.
- boolean droppedOnBubbleBar = notifyBubbleBarItemDropped();
// Upon successful drag, immediately stash taskbar.
// Note, this must be done last to ensure no AutohideSuspendFlags are active, as
// that will prevent us from stashing until the timeout.
@@ -563,12 +565,17 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
BubbleBarViewController bubbleBarViewController = bc.bubbleBarViewController;
boolean showingDropTarget = bubbleBarViewController.isShowingDropTarget();
if (showingDropTarget) {
- bubbleBarViewController.onItemDroppedInBubbleBarDragZone();
+ bubbleBarViewController.onItemDragCompleted();
}
return showingDropTarget;
}).orElse(false);
}
+ private void notifyBubbleBarItemDragCanceled() {
+ mControllers.bubbleControllers.ifPresent(bc ->
+ bc.bubbleBarViewController.onItemDraggedOutsideBubbleBarDropZone());
+ }
+
@Override
protected void endDrag() {
if (mDisallowGlobalDrag && !mIsDropHandledByDropTarget) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index 7a23006b4f..038e374a64 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -44,7 +44,6 @@ import com.android.launcher3.Utilities
import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_EDU_OPEN
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP
import com.android.launcher3.util.OnboardingPrefs.TASKBAR_SEARCH_EDU_SEEN
import com.android.launcher3.util.ResourceBasedOverride
@@ -319,7 +318,7 @@ open class TaskbarEduTooltipController(context: Context) :
fun maybeShowSearchEdu() {
if (
!enableTaskbarPinning() ||
- !DisplayController.isPinnedTaskbar(activityContext) ||
+ !activityContext.isPinnedTaskbar ||
!isTooltipEnabled ||
!shouldShowSearchEdu ||
userHasSeenSearchEdu ||
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
index b7000dbbb0..76489e0fa1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
@@ -19,6 +19,7 @@ import static android.view.MotionEvent.ACTION_HOVER_ENTER;
import static android.view.MotionEvent.ACTION_HOVER_EXIT;
import static android.view.View.ALPHA;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ACTION_POPUP;
import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER;
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS;
@@ -98,20 +99,18 @@ public class TaskbarHoverToolTipController implements View.OnHoverListener {
@Override
public boolean onHover(View v, MotionEvent event) {
- boolean isFolderOpen = AbstractFloatingView.hasOpenView(mActivity, TYPE_FOLDER);
// If hover leaves a taskbar icon animate the tooltip closed.
if (event.getAction() == ACTION_HOVER_EXIT) {
mHoverToolTipView.close(/* animate= */ false);
mActivity.setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, false);
- } else if (!isFolderOpen && event.getAction() == ACTION_HOVER_ENTER) {
- // Do not reveal if any floating views such as folders or edu pop-ups are open.
- revealHoverToolTip();
+ } else if (event.getAction() == ACTION_HOVER_ENTER) {
+ maybeRevealHoverToolTip();
mActivity.setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, true);
}
return false;
}
- private void revealHoverToolTip() {
+ private void maybeRevealHoverToolTip() {
if (mHoverView == null || mToolTipText == null) {
return;
}
@@ -122,6 +121,12 @@ public class TaskbarHoverToolTipController implements View.OnHoverListener {
if (mHoverView instanceof FolderIcon && !((FolderIcon) mHoverView).getIconVisible()) {
return;
}
+ // Do not reveal if floating views such as folders or app pop-ups are open,
+ // as these views will overlap and not look great.
+ if (AbstractFloatingView.hasOpenView(mActivity, TYPE_FOLDER | TYPE_ACTION_POPUP)) {
+ return;
+ }
+
Rect iconViewBounds = Utilities.getViewBounds(mHoverView);
mHoverToolTipView.showAtLocation(mToolTipText, iconViewBounds.centerX(),
mTaskbarView.getTop() - mYOffset, /* shouldAutoClose= */ false);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 3af2ab6d3c..cc6cc645f5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -51,7 +51,6 @@ import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATI
import com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
import com.android.launcher3.testing.shared.ResourceUtils
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.Executors
import java.io.PrintWriter
import kotlin.jvm.optionals.getOrNull
@@ -150,7 +149,7 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
if (
taskbarStashController.isInApp ||
controllers.uiController.isInOverviewUi ||
- DisplayController.showLockedTaskbarOnHome(context)
+ context.showLockedTaskbarOnHome()
) {
// only add the taskbar touch region if not on home
val bottom = windowLayoutParams.height
@@ -349,13 +348,17 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
controllers.bubbleControllers.isPresent &&
controllers.bubbleControllers.get().bubbleBarViewController.isBubbleBarVisible()
var insetsIsTouchableRegion = true
+ // Prevents the taskbar from taking touches and conflicting with setup wizard
if (
context.isPhoneButtonNavMode &&
+ context.isUserSetupComplete &&
(!controllers.navbarButtonsViewController.isImeVisible ||
!controllers.navbarButtonsViewController.isImeRenderingNavButtons)
) {
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME)
insetsIsTouchableRegion = false
+ debugTouchableRegion.lastSetTouchableReason =
+ "Phone button nav mode: Fullscreen touchable, IME not affecting nav buttons"
} else if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index b510e7ebb5..77c3e0a41c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -33,6 +33,7 @@ import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_IN_ANIM_A
import static com.android.launcher3.taskbar.bubbles.BubbleBarView.FADE_OUT_ANIM_POSITION_DURATION_MS;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
+import static com.android.quickstep.fallback.RecentsStateUtilsKt.toLauncherState;
import static com.android.quickstep.util.SystemUiFlagUtils.isTaskbarHidden;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_AWAKE;
@@ -40,7 +41,6 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.content.Context;
import android.os.SystemClock;
import android.util.Log;
import android.view.animation.Interpolator;
@@ -61,10 +61,13 @@ import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.BubbleLauncherState;
import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.fallback.RecentsState;
+import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
+import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ScalingWorkspaceRevealAnim;
import com.android.quickstep.util.SystemUiFlagUtils;
import com.android.quickstep.views.RecentsView;
@@ -76,6 +79,7 @@ import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.StringJoiner;
+import java.util.function.Consumer;
/**
* Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate
@@ -211,7 +215,7 @@ public class TaskbarLauncherStateController {
};
private final StateManager.StateListener<LauncherState> mStateListener =
- new StateManager.StateListener<LauncherState>() {
+ new StateManager.StateListener<>() {
@Override
public void onStateTransitionStart(LauncherState toState) {
@@ -225,11 +229,9 @@ public class TaskbarLauncherStateController {
updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, true);
if (!mShouldDelayLauncherStateAnim) {
if (toState == LauncherState.NORMAL) {
+ TaskbarActivityContext activity = mControllers.taskbarActivityContext;
boolean isPinnedTaskbarAndNotInDesktopMode =
- DisplayController.isPinnedTaskbar(
- mControllers.taskbarActivityContext)
- && !DisplayController.isInDesktopMode(
- mControllers.taskbarActivityContext);
+ !activity.isInDesktopMode() && activity.isPinnedTaskbar();
applyState(QuickstepTransitionManager.getTaskbarToHomeDuration(
isPinnedTaskbarAndNotInDesktopMode));
} else {
@@ -247,6 +249,20 @@ public class TaskbarLauncherStateController {
}
};
+ private final StateManager.StateListener<RecentsState> mRecentsStateListener =
+ new StateManager.StateListener<>() {
+
+ @Override
+ public void onStateTransitionStart(RecentsState toState) {
+ mStateListener.onStateTransitionStart(toLauncherState(toState));
+ }
+
+ @Override
+ public void onStateTransitionComplete(RecentsState finalState) {
+ mStateListener.onStateTransitionComplete(toLauncherState(finalState));
+ }
+ };
+
/**
* Callback for when launcher state transition completes after user swipes to home.
* @param finalState The final state of the transition.
@@ -282,6 +298,8 @@ public class TaskbarLauncherStateController {
if (!mControllers.taskbarActivityContext.isPhoneMode()) {
mLauncher.getStateManager().addStateListener(mStateListener);
+ runForRecentsWindowManager(recentsWindowManager ->
+ recentsWindowManager.getStateManager().addStateListener(mRecentsStateListener));
}
mLauncherState = launcher.getStateManager().getState();
updateStateForSysuiFlags(sysuiStateFlags, /*applyState*/ false);
@@ -306,6 +324,8 @@ public class TaskbarLauncherStateController {
mLauncher.getHotseat().setIconsAlpha(1f, ALPHA_CHANNEL_TASKBAR_ALIGNMENT);
mLauncher.getStateManager().removeStateListener(mStateListener);
+ runForRecentsWindowManager(recentsWindowManager ->
+ recentsWindowManager.getStateManager().removeStateListener(mRecentsStateListener));
mCanSyncViews = !mControllers.taskbarActivityContext.isPhoneMode();
mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
@@ -344,12 +364,15 @@ public class TaskbarLauncherStateController {
}
mTaskBarRecentsAnimationListener = new TaskBarRecentsAnimationListener(callbacks);
callbacks.addListener(mTaskBarRecentsAnimationListener);
- ((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(() ->
- mTaskBarRecentsAnimationListener.endGestureStateOverride(true, false /*canceled*/));
+ RecentsView recentsView = mControllers.uiController.getRecentsView();
+ if (recentsView != null) {
+ recentsView.setTaskLaunchListener(() -> mTaskBarRecentsAnimationListener
+ .endGestureStateOverride(true, false /*canceled*/));
+ recentsView.setTaskLaunchCancelledRunnable(() -> {
+ updateStateForUserFinishedToApp(false /* finishedToApp */);
+ });
+ }
- ((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchCancelledRunnable(() -> {
- updateStateForUserFinishedToApp(false /* finishedToApp */);
- });
return animatorSet;
}
@@ -475,8 +498,8 @@ public class TaskbarLauncherStateController {
final boolean isIconAlignedWithHotseat = isIconAlignedWithHotseat();
final float toAlignment = isIconAlignedWithHotseat ? 1 : 0;
boolean handleOpenFloatingViews = false;
- boolean isPinnedTaskbar = DisplayController.isPinnedTaskbar(
- mControllers.taskbarActivityContext);
+ boolean isPinnedTaskbar =
+ mControllers.taskbarActivityContext.isPinnedTaskbar();
if (DEBUG) {
Log.d(TAG, "onStateChangeApplied - isInLauncher: " + isInLauncher
+ ", mLauncherState: " + mLauncherState
@@ -590,7 +613,8 @@ public class TaskbarLauncherStateController {
float backgroundAlpha = isInLauncher && isTaskbarAlignedWithHotseat() ? 0 : 1;
AnimatedFloat taskbarBgOffset =
mControllers.taskbarDragLayerController.getTaskbarBackgroundOffset();
- boolean showTaskbar = shouldShowTaskbar(mLauncher, isInLauncher, isInOverview);
+ boolean showTaskbar = shouldShowTaskbar(mControllers.taskbarActivityContext, isInLauncher,
+ isInOverview);
float taskbarBgOffsetEnd = showTaskbar ? 0f : 1f;
float taskbarBgOffsetStart = showTaskbar ? 1f : 0f;
@@ -727,13 +751,13 @@ public class TaskbarLauncherStateController {
return animatorSet;
}
- private static boolean shouldShowTaskbar(Context context, boolean isInLauncher,
- boolean isInOverview) {
- if (DisplayController.showDesktopTaskbarForFreeformDisplay(context)) {
+ private static boolean shouldShowTaskbar(TaskbarActivityContext activityContext,
+ boolean isInLauncher, boolean isInOverview) {
+ if (activityContext.showDesktopTaskbarForFreeformDisplay()) {
return true;
}
- if (DisplayController.showLockedTaskbarOnHome(context) && isInLauncher) {
+ if (activityContext.showLockedTaskbarOnHome() && isInLauncher) {
return true;
}
return !isInLauncher || isInOverview;
@@ -788,11 +812,11 @@ public class TaskbarLauncherStateController {
* This refers to the intended state - a transition to this state might be in progress.
*/
public boolean isTaskbarAlignedWithHotseat() {
- if (DisplayController.showDesktopTaskbarForFreeformDisplay(mLauncher)) {
+ if (mControllers.taskbarActivityContext.showDesktopTaskbarForFreeformDisplay()) {
return false;
}
- if (DisplayController.showLockedTaskbarOnHome(mLauncher) && isInLauncher()) {
+ if (mControllers.taskbarActivityContext.showLockedTaskbarOnHome() && isInLauncher()) {
return false;
}
@@ -826,6 +850,15 @@ public class TaskbarLauncherStateController {
return mLauncherState.isRecentsViewVisible;
}
+ /**
+ * Returns the current mLauncherState. Note that this could represent RecentsState as well, as
+ * we convert those to equivalent LauncherStates even if Launcher Activity is not actually in
+ * those states (for the case where the state is represented in a separate Window instead).
+ */
+ public LauncherState getLauncherState() {
+ return mLauncherState;
+ }
+
private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
boolean committed) {
boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
@@ -1063,7 +1096,10 @@ public class TaskbarLauncherStateController {
boolean canceled) {
mCallbacks.removeListener(this);
mTaskBarRecentsAnimationListener = null;
- ((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(null);
+ RecentsView recentsView = mControllers.uiController.getRecentsView();
+ if (recentsView != null) {
+ recentsView.setTaskLaunchListener(null);
+ }
if (mSkipNextRecentsAnimEnd && !canceled) {
mSkipNextRecentsAnimEnd = false;
@@ -1102,6 +1138,20 @@ public class TaskbarLauncherStateController {
controller.applyState();
}
+ /**
+ * Helper function to run a callback on the RecentsWindowManager (if it exists).
+ */
+ private void runForRecentsWindowManager(Consumer<RecentsWindowManager> callback) {
+ if (RecentsWindowFlags.getEnableOverviewInWindow()) {
+ final TaskbarActivityContext taskbarContext = mControllers.taskbarActivityContext;
+ RecentsWindowManager recentsWindowManager = RecentsDisplayModel.getINSTANCE()
+ .get(taskbarContext).getRecentsWindowManager(taskbarContext.getDisplayId());
+ if (recentsWindowManager != null) {
+ callback.accept(recentsWindowManager);
+ }
+ }
+ }
+
private static String getStateString(int flags) {
StringJoiner result = new StringJoiner("|");
appendFlag(result, flags, FLAG_VISIBLE, "flag_visible");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 0fff0aa14d..f3cbdb5a2a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -17,6 +17,8 @@ package com.android.launcher3.taskbar;
import static android.content.Context.RECEIVER_EXPORTED;
import static android.content.Context.RECEIVER_NOT_EXPORTED;
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -80,6 +82,8 @@ import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.AllAppsActionManager;
import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.SystemDecorationChangeObserver;
+import com.android.quickstep.SystemDecorationChangeObserver.DisplayDecorationListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.fallback.window.RecentsWindowFlags;
@@ -87,6 +91,7 @@ import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ContextualSearchInvoker;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.RecentsViewContainer;
+import com.android.server.am.Flags;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.statusbar.phone.BarTransitions;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -104,7 +109,7 @@ import java.util.StringJoiner;
/**
* Class to manage taskbar lifecycle
*/
-public class TaskbarManager {
+public class TaskbarManager implements DisplayDecorationListener {
private static final String TAG = "TaskbarManager";
private static final boolean DEBUG = false;
private static final int TASKBAR_DESTROY_DURATION = 100;
@@ -135,6 +140,7 @@ public class TaskbarManager {
Settings.Secure.NAV_BAR_KIDS_MODE);
private final Context mBaseContext;
+ private final int mPrimaryDisplayId;
private final TaskbarNavButtonCallbacks mNavCallbacks;
// TODO: Remove this during the connected displays lifecycle refactor.
private final Context mPrimaryWindowContext;
@@ -167,6 +173,10 @@ public class TaskbarManager {
private final SparseArray<DeviceProfile> mExternalDeviceProfiles = new SparseArray<>();
private StatefulActivity mActivity;
private RecentsViewContainer mRecentsViewContainer;
+ /** Whether this device is a desktop android device **/
+ private boolean mIsAndroidPC;
+ /** Whether this device supports freeform windows management. Can change dynamically **/
+ private boolean mSupportsFreeformWindowsManagement;
/**
* Cache a copy here so we can initialize state whenever taskbar is recreated, since
@@ -189,29 +199,29 @@ public class TaskbarManager {
private class RecreationListener implements DisplayController.DisplayInfoChangeListener {
@Override
public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
+ int displayId = context.getDisplayId();
if ((flags & CHANGE_DENSITY) != 0) {
- debugTaskbarManager("onDisplayInfoChanged: Display density changed",
- context.getDisplayId());
+ debugTaskbarManager("onDisplayInfoChanged: Display density changed", displayId);
}
if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
- debugTaskbarManager("onDisplayInfoChanged: Navigation mode changed",
- context.getDisplayId());
+ debugTaskbarManager("onDisplayInfoChanged: Navigation mode changed", displayId);
}
if ((flags & CHANGE_DESKTOP_MODE) != 0) {
debugTaskbarManager("onDisplayInfoChanged: Desktop mode changed",
context.getDisplayId());
+ handleDisplayUpdatesForPerceptibleTasks();
}
if ((flags & CHANGE_TASKBAR_PINNING) != 0) {
- debugTaskbarManager("onDisplayInfoChanged: Taskbar pinning changed",
- context.getDisplayId());
+ debugTaskbarManager("onDisplayInfoChanged: Taskbar pinning changed", displayId);
}
if ((flags & (CHANGE_DENSITY | CHANGE_NAVIGATION_MODE | CHANGE_DESKTOP_MODE
| CHANGE_TASKBAR_PINNING | CHANGE_SHOW_LOCKED_TASKBAR)) != 0) {
- debugTaskbarManager("onDisplayInfoChanged: Recreating Taskbar!",
- context.getDisplayId());
+
TaskbarActivityContext taskbarActivityContext = getCurrentActivityContext();
if ((flags & CHANGE_SHOW_LOCKED_TASKBAR) != 0) {
+ debugTaskbarManager("onDisplayInfoChanged: show locked taskbar changed!",
+ displayId);
recreateTaskbars();
} else if ((flags & CHANGE_DESKTOP_MODE) != 0) {
if (mShouldIgnoreNextDesktopModeChangeFromDisplayController) {
@@ -220,7 +230,7 @@ public class TaskbarManager {
}
// Only Handles Special Exit Cases for Desktop Mode Taskbar Recreation.
if (taskbarActivityContext != null
- && !DisplayController.showLockedTaskbarOnHome(context)) {
+ && !taskbarActivityContext.showLockedTaskbarOnHome()) {
recreateTaskbars();
}
} else {
@@ -235,7 +245,7 @@ public class TaskbarManager {
recreateTaskbars();
};
- private final PerceptibleTaskListener mTaskStackListener;
+ private PerceptibleTaskListener mTaskStackListener;
private class PerceptibleTaskListener implements TaskStackChangeListener {
private ArraySet<Integer> mPerceptibleTasks = new ArraySet<Integer>();
@@ -289,6 +299,14 @@ public class TaskbarManager {
public void onTaskRemoved(int taskId) {
mPerceptibleTasks.remove(taskId);
}
+
+ public void unregisterListener() {
+ for (Integer taskId : mPerceptibleTasks) {
+ ActivityManagerWrapper.getInstance().setTaskIsPerceptible(taskId, false);
+ }
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+ mTaskStackListener);
+ }
}
private final DesktopVisibilityController.TaskbarDesktopModeListener
@@ -298,6 +316,11 @@ public class TaskbarManager {
public void onExitDesktopMode(int duration) {
for (int taskbarIndex = 0; taskbarIndex < mTaskbars.size(); taskbarIndex++) {
int displayId = mTaskbars.keyAt(taskbarIndex);
+ if (DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
+ && !isDefaultDisplay(displayId)) {
+ continue;
+ }
+
TaskbarActivityContext taskbarActivityContext = getTaskbarForDisplay(
displayId);
if (taskbarActivityContext != null
@@ -317,6 +340,11 @@ public class TaskbarManager {
public void onEnterDesktopMode(int duration) {
for (int taskbarIndex = 0; taskbarIndex < mTaskbars.size(); taskbarIndex++) {
int displayId = mTaskbars.keyAt(taskbarIndex);
+ if (DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
+ && !isDefaultDisplay(displayId)) {
+ continue;
+ }
+
TaskbarActivityContext taskbarActivityContext = getTaskbarForDisplay(
displayId);
if (taskbarActivityContext != null) {
@@ -338,7 +366,6 @@ public class TaskbarManager {
}
};
-
private boolean mUserUnlocked = false;
private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver;
@@ -410,26 +437,28 @@ public class TaskbarManager {
TaskbarNavButtonCallbacks navCallbacks,
RecentsDisplayModel recentsDisplayModel) {
mBaseContext = context;
+ mPrimaryDisplayId = mBaseContext.getDisplayId();
mAllAppsActionManager = allAppsActionManager;
mNavCallbacks = navCallbacks;
mRecentsDisplayModel = recentsDisplayModel;
// Set up primary display.
- int primaryDisplayId = getDefaultDisplayId();
debugPrimaryTaskbar("TaskbarManager constructor");
- mPrimaryWindowContext = createWindowContext(primaryDisplayId);
+ mPrimaryWindowContext = createWindowContext(getDefaultDisplayId());
mPrimaryWindowManager = mPrimaryWindowContext.getSystemService(WindowManager.class);
DesktopVisibilityController.INSTANCE.get(
mPrimaryWindowContext).registerTaskbarDesktopModeListener(
mTaskbarDesktopModeListener);
- createTaskbarRootLayout(primaryDisplayId);
- createNavButtonController(primaryDisplayId);
- createAndRegisterComponentCallbacks(primaryDisplayId);
+ createTaskbarRootLayout(getDefaultDisplayId());
+ createNavButtonController(getDefaultDisplayId());
+ createAndRegisterComponentCallbacks(getDefaultDisplayId());
SettingsCache.INSTANCE.get(mPrimaryWindowContext)
.register(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener);
SettingsCache.INSTANCE.get(mPrimaryWindowContext)
.register(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
+ SystemDecorationChangeObserver.getINSTANCE().get(mPrimaryWindowContext)
+ .registerDisplayDecorationListener(this);
mShutdownReceiver =
new SimpleBroadcastReceiver(
mPrimaryWindowContext, UI_HELPER_EXECUTOR, i -> destroyAllTaskbars());
@@ -458,7 +487,10 @@ public class TaskbarManager {
mTaskbarBroadcastReceiver.register(RECEIVER_NOT_EXPORTED, ACTION_SHOW_TASKBAR);
});
- if (ActivityManagerWrapper.usePerceptibleTasks(getPrimaryWindowContext())) {
+ mIsAndroidPC = getPrimaryWindowContext().getPackageManager().hasSystemFeature(FEATURE_PC);
+ mSupportsFreeformWindowsManagement = getFreeformWindowsManagementInfo();
+
+ if (eligibleForPerceptibleTasks()) {
mTaskStackListener = new PerceptibleTaskListener();
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
} else {
@@ -468,6 +500,30 @@ public class TaskbarManager {
debugPrimaryTaskbar("TaskbarManager created");
}
+ private void handleDisplayUpdatesForPerceptibleTasks() {
+ // 1. When desktop mode changes, detect eligibility for perceptible tasks.
+ // 2. When no longer eligible for perceptible tasks, turn off and clean up.
+ mSupportsFreeformWindowsManagement = getFreeformWindowsManagementInfo();
+ if (eligibleForPerceptibleTasks()) {
+ if (mTaskStackListener == null) {
+ mTaskStackListener = new PerceptibleTaskListener();
+ TaskStackChangeListeners.getInstance()
+ .registerTaskStackListener(mTaskStackListener);
+ }
+ } else {
+ // not eligible for perceptible tasks, so we should unregister the listener
+ if (mTaskStackListener != null) {
+ mTaskStackListener.unregisterListener();
+ mTaskStackListener = null;
+ }
+ }
+ }
+
+ private boolean getFreeformWindowsManagementInfo() {
+ return getPrimaryWindowContext().getPackageManager().hasSystemFeature(
+ FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+ }
+
private void destroyAllTaskbars() {
debugPrimaryTaskbar("destroyAllTaskbars");
for (int i = 0; i < mTaskbars.size(); i++) {
@@ -646,7 +702,9 @@ public class TaskbarManager {
private TaskbarUIController createTaskbarUIControllerForRecentsViewContainer(
RecentsViewContainer container) {
debugPrimaryTaskbar("createTaskbarUIControllerForRecentsViewContainer");
- if (container instanceof QuickstepLauncher quickstepLauncher) {
+ if (mActivity instanceof QuickstepLauncher quickstepLauncher) {
+ // If 1P Launcher is default, always use LauncherTaskbarUIController, regardless of
+ // whether the recents container is NexusLauncherActivity or RecentsWindowManager.
return new LauncherTaskbarUIController(quickstepLauncher);
}
// If a 3P Launcher is default, always use FallbackTaskbarUIController regardless of
@@ -904,6 +962,7 @@ public class TaskbarManager {
* Signal from SysUI indicating that a non-mirroring display was just connected to the
* primary device or a previously mirroring display is switched to extended mode.
*/
+ @Override
public void onDisplayAddSystemDecorations(int displayId) {
debugTaskbarManager("onDisplayAddSystemDecorations: ", displayId);
Display display = getDisplay(displayId);
@@ -967,6 +1026,7 @@ public class TaskbarManager {
* Signal from SysUI indicating that a previously connected non-mirroring display was just
* removed from the primary device.
*/
+ @Override
public void onDisplayRemoved(int displayId) {
debugTaskbarManager("onDisplayRemoved: ", displayId);
if (!DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue() || isDefaultDisplay(
@@ -988,12 +1048,12 @@ public class TaskbarManager {
displayId);
removeAndUnregisterComponentCallbacks(displayId);
- debugTaskbarManager("onDisplayRemoved: destroying Taskbar!", displayId);
- destroyTaskbarForDisplay(displayId);
-
debugTaskbarManager("onDisplayRemoved: removing DeviceProfile from map!", displayId);
removeDeviceProfileFromMap(displayId);
+ debugTaskbarManager("onDisplayRemoved: destroying Taskbar!", displayId);
+ destroyTaskbarForDisplay(displayId);
+
debugTaskbarManager("onDisplayRemoved: removing WindowContext from map!", displayId);
removeWindowContextFromMap(displayId);
@@ -1006,6 +1066,7 @@ public class TaskbarManager {
/**
* Signal from SysUI indicating that system decorations should be removed from the display.
*/
+ @Override
public void onDisplayRemoveSystemDecorations(int displayId) {
// The display mirroring starts. The handling logic is the same as when removing a
// display.
@@ -1049,20 +1110,31 @@ public class TaskbarManager {
.unregister(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener);
SettingsCache.INSTANCE.get(mPrimaryWindowContext)
.unregister(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
+ SystemDecorationChangeObserver.getINSTANCE().get(mPrimaryWindowContext)
+ .unregisterDisplayDecorationListener(this);
debugPrimaryTaskbar("destroy: unregistering component callbacks");
removeAndUnregisterComponentCallbacks(getDefaultDisplayId());
mShutdownReceiver.unregisterReceiverSafely();
- if (ActivityManagerWrapper.usePerceptibleTasks(getPrimaryWindowContext())) {
- for (Integer taskId : mTaskStackListener.mPerceptibleTasks) {
- ActivityManagerWrapper.getInstance().setTaskIsPerceptible(taskId, false);
- }
+ if (mTaskStackListener != null) {
+ mTaskStackListener.unregisterListener();
}
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+
debugPrimaryTaskbar("destroy: destroying all taskbars!");
destroyAllTaskbars();
debugPrimaryTaskbar("destroy: finished!");
}
+ private boolean eligibleForPerceptibleTasks() {
+ // Perceptible tasks feature (oom boosting) is eligible for android PC devices, and
+ // other android devices that supports free form windows
+ //
+ // - isAndroidPC is set per device (in this case, desktop devices)
+ // - supportsFreeformWindowsManagement is dynamic, and is to be used for the use-case where
+ // user plugs in their device to external displays
+ return Flags.perceptibleTasks()
+ && (mIsAndroidPC || mSupportsFreeformWindowsManagement);
+ }
+
public @Nullable TaskbarActivityContext getCurrentActivityContext() {
return getTaskbarForDisplay(getDefaultDisplayId());
}
@@ -1168,7 +1240,7 @@ public class TaskbarManager {
* @return The {@link TaskbarActivityContext} for the specified display, or
* {@code null} if no taskbar is associated with that display.
*/
- private TaskbarActivityContext getTaskbarForDisplay(int displayId) {
+ public TaskbarActivityContext getTaskbarForDisplay(int displayId) {
return mTaskbars.get(displayId);
}
@@ -1607,7 +1679,7 @@ public class TaskbarManager {
}
private int getDefaultDisplayId() {
- return mBaseContext.getDisplayId();
+ return mPrimaryDisplayId;
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 1a6cd6042e..7f240bd781 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -83,7 +83,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
// Initialized in init.
private TaskbarControllers mControllers;
private boolean mAllowInitialSplitSelection;
- private AppInfo[] mAppInfosList;
+ private AppInfo[] mAppInfosList = AppInfo.EMPTY_ARRAY;
// Saves the ItemInfos in the hotseat without the predicted items.
private SparseArray<ItemInfo> mHotseatInfosList;
private ManageWindowsTaskbarShortcut<BaseTaskbarContext> mManageWindowsTaskbarShortcut;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 5284edd620..e597148462 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -23,10 +23,12 @@ import static com.android.app.animation.Interpolators.INSTANT;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.internal.jank.InteractionJankMonitor.Configuration;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
+import static com.android.launcher3.Flags.syncAppLaunchWithTaskbarStash;
import static com.android.launcher3.QuickstepTransitionManager.PINNED_TASKBAR_TRANSITION_DURATION;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW;
+import static com.android.launcher3.taskbar.TaskbarActivityContext.ENABLE_TASKBAR_BEHIND_SHADE;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
@@ -64,7 +66,6 @@ import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.SystemUiFlagUtils;
@@ -107,6 +108,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
// taskbar should always be stashed for bubble bar on phone
public static final int FLAG_STASHED_BUBBLE_BAR_ON_PHONE = 1 << 15;
+ public static final int FLAG_IGNORE_IN_APP = 1 << 16; // used to sync with app launch animation
+
// If any of these flags are enabled, isInApp should return true.
private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
@@ -303,8 +306,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
// TODO(b/390665752): Feature to "lock" pinned taskbar to home screen will be superseded by
// pinning, in other launcher states, at which point this variable can be removed.
mInAppStateAffectsDesktopTasksVisibilityInTaskbar =
- !DisplayController.showDesktopTaskbarForFreeformDisplay(mActivity)
- && DisplayController.showLockedTaskbarOnHome(mActivity);
+ !mActivity.showDesktopTaskbarForFreeformDisplay()
+ && mActivity.showLockedTaskbarOnHome();
mTaskbarBackgroundDuration = activity.getResources().getInteger(
R.integer.taskbar_background_duration);
@@ -409,7 +412,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
* Returns how long the stash/unstash animation should play.
*/
public long getStashDuration() {
- if (DisplayController.isPinnedTaskbar(mActivity)) {
+ if (mActivity.isPinnedTaskbar()) {
return PINNED_TASKBAR_TRANSITION_DURATION;
}
return mActivity.isTransientTaskbar() ? TRANSIENT_TASKBAR_STASH_DURATION
@@ -1135,7 +1138,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
long startDelay = 0;
updateStateForFlag(FLAG_STASHED_IN_APP_SYSUI, hasAnyFlag(systemUiStateFlags,
- SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE | SYSUI_STATE_DIALOG_SHOWING));
+ SYSUI_STATE_DIALOG_SHOWING | (ENABLE_TASKBAR_BEHIND_SHADE.isTrue()
+ ? 0
+ : SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE)
+ ));
boolean stashForBubbles = hasAnyFlag(FLAG_IN_OVERVIEW)
&& hasAnyFlag(systemUiStateFlags, SYSUI_STATE_BUBBLES_EXPANDED)
@@ -1179,7 +1185,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
}
// Do not stash if pinned taskbar, hardware keyboard is attached and no IME is docked
- if (mActivity.isHardwareKeyboard() && DisplayController.isPinnedTaskbar(mActivity)
+ if (mActivity.isHardwareKeyboard() && mActivity.isPinnedTaskbar()
&& !mActivity.isImeDocked()) {
return false;
}
@@ -1412,6 +1418,13 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
*/
@Nullable
public Animator createSetStateAnimator(long flags, long duration) {
+ // We do this when we want to synchronize the app launch and taskbar stash animations.
+ if (syncAppLaunchWithTaskbarStash()
+ && hasAnyFlag(FLAG_IGNORE_IN_APP)
+ && hasAnyFlag(flags, FLAG_IN_APP)) {
+ flags = flags & ~FLAG_IN_APP;
+ }
+
boolean isStashed = mStashCondition.test(flags);
if (DEBUG) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 061a5a127e..b567871b92 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -21,6 +21,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
+import android.animation.Animator;
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.view.MotionEvent;
@@ -38,6 +39,8 @@ import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.taskbar.bubbles.BubbleBarController;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.util.SplitTask;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskContainer;
@@ -455,4 +458,18 @@ public class TaskbarUIController implements BubbleBarController.BubbleBarLocatio
*/
public void onSwipeToUnstashTaskbar() {
}
+
+ /**
+ * Called at the end of a gesture (see {@link GestureState.GestureEndTarget}).
+ * @param endTarget Where the gesture animation is going to.
+ * @param callbacks callbacks to track the recents animation lifecycle. The state change is
+ * automatically reset once the recents animation finishes
+ * @return An optional Animator to play in parallel with the default gesture end animation.
+ */
+ public @Nullable Animator getParallelAnimationToGestureEndTarget(
+ GestureState.GestureEndTarget endTarget,
+ long duration,
+ RecentsAnimationCallbacks callbacks) {
+ return null;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 4c94432bd9..b2989cd81a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -368,23 +368,9 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
if (!(view.getTag() instanceof CollectionInfo)) {
mActivityContext.getViewCache().recycleView(view.getSourceLayoutResId(), view);
}
- if (view instanceof FolderIcon fi) {
- // We should clear FolderInfo's Folder and FolderIcon to avoid memory leak.
- fi.removeListeners();
- }
view.setTag(null);
}
- /** Loop through all {@link FolderIcon} as child views and clear listeners to avoid leak. */
- public void removeFolderIconListeners() {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- if (getChildAt(i) instanceof FolderIcon fi) {
- fi.removeListeners();
- }
- }
- }
-
/** Inflates/binds the hotseat items and recent tasks to the view. */
protected void updateItems(ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
if (mActivityContext.isDestroyed()) return;
@@ -445,7 +431,6 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
private void updateItemsWithLayoutTransition(
ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
-
if (mNumStaticViews == 0) {
mNumStaticViews = addStaticViews();
}
@@ -579,6 +564,9 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
LayoutParams lp = new LayoutParams(mIconTouchSize, mIconTouchSize);
hotseatView.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
addView(hotseatView, mNextViewIndex, lp);
+ } else if (hotseatView instanceof FolderIcon fi) {
+ fi.onItemsChanged(false);
+ fi.getFolder().reapplyItemInfo();
}
// Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index dcb9fbff42..d0886e0ca1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -36,7 +36,6 @@ import androidx.annotation.Nullable;
import com.android.internal.jank.Cuj;
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
-import com.android.launcher3.util.DisplayController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
@@ -67,8 +66,8 @@ public class TaskbarViewCallbacks {
InteractionJankMonitorWrapper.begin(v, Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS,
/* tag= */ "TASKBAR_BUTTON");
mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP);
- if (DisplayController.showLockedTaskbarOnHome(mActivity)
- || DisplayController.showDesktopTaskbarForFreeformDisplay(mActivity)) {
+ if (mActivity.showLockedTaskbarOnHome()
+ || mActivity.showDesktopTaskbarForFreeformDisplay()) {
// If the taskbar can be shown on the home screen, use mAllAppsToggler to toggle all
// apps, which will toggle the launcher activity all apps when on home screen.
// TODO(b/395913143): Reconsider this if a gap in taskbar all apps functionality that
@@ -248,8 +247,7 @@ public class TaskbarViewCallbacks {
/** Returns true if the taskbar pinning popup view was shown for {@code event}. */
private boolean maybeShowPinningView(@NonNull MotionEvent event) {
- if (!DisplayController.isPinnedTaskbar(mActivity) || mTaskbarView.isEventOverAnyItem(
- event)) {
+ if (!mActivity.isPinnedTaskbar() || mTaskbarView.isEventOverAnyItem(event)) {
return false;
}
mControllers.taskbarPinningController.showPinningView(mTaskbarView, event.getRawX());
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacksFactory.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacksFactory.kt
index 17da5336f4..6e948894c2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacksFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacksFactory.kt
@@ -42,7 +42,8 @@ open class TaskbarViewCallbacksFactory : ResourceBasedOverride {
if (contextualSearchInvoked) {
val runningPackage =
TopTaskTracker.INSTANCE[activity].getCachedTopTask(
- /* filterOnlyVisibleRecents */ true
+ /* filterOnlyVisibleRecents */ true,
+ activity.display.displayId,
)
.getPackageName()
activity.statsLogManager
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 605171a7aa..ac9d2ba8bf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -19,6 +19,7 @@ import static android.animation.LayoutTransition.APPEARING;
import static android.animation.LayoutTransition.CHANGE_APPEARING;
import static android.animation.LayoutTransition.CHANGE_DISAPPEARING;
import static android.animation.LayoutTransition.DISAPPEARING;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DesktopModeFlags.ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION;
import static com.android.app.animation.Interpolators.EMPHASIZED;
@@ -86,7 +87,6 @@ import com.android.launcher3.taskbar.bubbles.BubbleBarController;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.taskbar.customization.TaskbarAllAppsButtonContainer;
import com.android.launcher3.taskbar.customization.TaskbarDividerContainer;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.MultiPropertyFactory;
@@ -393,7 +393,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
if (enableTaskbarPinning()) {
mTaskbarView.removeOnLayoutChangeListener(mTaskbarViewLayoutChangeListener);
}
- mTaskbarView.removeFolderIconListeners();
LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
}
@@ -496,7 +495,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
View[] iconViews = mTaskbarView.getIconViews();
float finalScale;
- if (mControllers.getSharedState().startTaskbarVariantIsTransient) {
+ TaskbarSharedState sharedState = mControllers.getSharedState();
+ if (sharedState != null && sharedState.startTaskbarVariantIsTransient) {
finalScale = mapRange(scale, 1f, ((float) mPersistentIconSize / mTransientIconSize));
} else {
finalScale = mapRange(scale, ((float) mTransientIconSize / mPersistentIconSize), 1f);
@@ -748,7 +748,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
}
private boolean shouldUpdateIconContentDescription(BubbleTextView btv) {
- boolean isInDesktopMode = mControllers.taskbarDesktopModeController.isInDesktopMode();
+ boolean isInDesktopMode = mControllers.taskbarDesktopModeController.isInDesktopMode(
+ DEFAULT_DISPLAY);
boolean isAllAppsButton = btv instanceof TaskbarAllAppsButtonContainer;
boolean isDividerButton = btv instanceof TaskbarDividerContainer;
return isInDesktopMode && !isAllAppsButton && !isDividerButton;
@@ -931,7 +932,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) {
PendingAnimation setter = new PendingAnimation(100);
// icon alignment not needed for pinned taskbar.
- if (DisplayController.isPinnedTaskbar(mActivity)) return setter.createPlaybackController();
+ if (mActivity.isPinnedTaskbar()) {
+ return setter.createPlaybackController();
+ }
mOnControllerPreCreateCallback.run();
DeviceProfile taskbarDp = mActivity.getDeviceProfile();
Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
@@ -949,7 +952,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
// or fade in while already in in-app state.
Interpolator interpolator = mIsHotseatIconOnTopWhenAligned ? LINEAR : FINAL_FRAME;
- int offsetY = launcherDp.getTaskbarOffsetY();
+ int offsetY = taskbarDp.getTaskbarOffsetY();
setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, interpolator);
setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, interpolator);
setter.setFloat(mTaskbarNavButtonTranslationYForInAppDisplay, VALUE, offsetY, interpolator);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index d43ebe2977..6380c2371b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -18,6 +18,7 @@ package com.android.launcher3.taskbar.bubbles;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -204,7 +205,7 @@ public class BubbleBarView extends FrameLayout {
mExpandedBarIconsSpacing = getResources().getDimensionPixelSize(
R.dimen.bubblebar_expanded_icon_spacing);
mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation);
- mDragElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_drag_elevation);
+ mDragElevation = getResources().getDimensionPixelSize(R.dimen.dragged_bubble_elevation);
mPointerSize = getResources()
.getDimensionPixelSize(R.dimen.bubblebar_pointer_visible_size);
@@ -560,30 +561,52 @@ public class BubbleBarView extends FrameLayout {
// First animator hides the bar.
// After it completes, bubble positions in the bar and arrow position is updated.
// Second animator is started to show the bar.
- ObjectAnimator alphaOutAnim = ObjectAnimator.ofFloat(
- this, getLocationAnimAlphaProperty(), 0f);
- mBubbleBarLocationAnimator = BarsLocationAnimatorHelper.getBubbleBarLocationOutAnimator(
- this,
- bubbleBarLocation,
- alphaOutAnim);
+ mBubbleBarLocationAnimator = animateToBubbleBarLocationOut(bubbleBarLocation);
mBubbleBarLocationAnimator.addListener(AnimatorListeners.forEndCallback(() -> {
- updateBubblesLayoutProperties(bubbleBarLocation);
- mBubbleBarBackground.setAnchorLeft(bubbleBarLocation.isOnLeft(isLayoutRtl()));
- ObjectAnimator alphaInAnim = ObjectAnimator.ofFloat(BubbleBarView.this,
- getLocationAnimAlphaProperty(), 1f);
-
// Animate it in
- mBubbleBarLocationAnimator = BarsLocationAnimatorHelper.getBubbleBarLocationInAnimator(
- bubbleBarLocation,
- mBubbleBarLocation,
- getDistanceFromOtherSide(),
- alphaInAnim,
- BubbleBarView.this);
+ mBubbleBarLocationAnimator = animateToBubbleBarLocationIn(mBubbleBarLocation,
+ bubbleBarLocation);
mBubbleBarLocationAnimator.start();
}));
mBubbleBarLocationAnimator.start();
}
+ /** Creates animator for animating bubble bar in. */
+ public Animator animateToBubbleBarLocationIn(BubbleBarLocation fromLocation,
+ BubbleBarLocation toLocation) {
+ updateBubblesLayoutProperties(toLocation);
+ mBubbleBarBackground.setAnchorLeft(toLocation.isOnLeft(isLayoutRtl()));
+ ObjectAnimator alphaInAnim = ObjectAnimator.ofFloat(BubbleBarView.this,
+ getLocationAnimAlphaProperty(), 1f);
+ return BarsLocationAnimatorHelper.getBubbleBarLocationInAnimator(toLocation, fromLocation,
+ getDistanceFromOtherSide(), alphaInAnim, this);
+ }
+
+ /**
+ * Creates animator for animating bubble bar out.
+ *
+ * @param targetLocation the location bubble br should animate to.
+ */
+ public Animator animateToBubbleBarLocationOut(BubbleBarLocation targetLocation) {
+ ObjectAnimator alphaOutAnim = ObjectAnimator.ofFloat(
+ this, getLocationAnimAlphaProperty(), 0f);
+ Animator outAnimation = BarsLocationAnimatorHelper.getBubbleBarLocationOutAnimator(
+ this,
+ targetLocation,
+ alphaOutAnim);
+ outAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
+ // need to restore the original bar view state in case icon is dropped to the bubble
+ // bar original location
+ updateBubblesLayoutProperties(targetLocation);
+ mBubbleBarBackground.setAnchorLeft(targetLocation.isOnLeft(isLayoutRtl()));
+ setTranslationX(0f);
+ }
+ });
+ return outAnimation;
+ }
+
/**
* Get property that can be used to animate the alpha value for the bar.
* When a bubble is being dragged, uses {@link #BUBBLE_DRAG_ALPHA}.
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 7fb6480005..ce4a14f551 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -194,9 +194,11 @@ public class BubbleBarViewController {
private boolean mHiddenForStashed;
private boolean mShouldShowEducation;
public boolean mOverflowAdded;
- private boolean mIsLocationUpdatedForDropTarget = false;
private boolean mWasStashedBeforeEnteringBubbleDragZone = false;
+ /** This field is used solely to track the bubble bar location prior to the start of the drag */
+ private @Nullable BubbleBarLocation mBubbleBarDragLocation;
+
private BubbleBarViewAnimator mBubbleBarViewAnimator;
private final FrameLayout mBubbleBarContainer;
private BubbleBarFlyoutController mBubbleBarFlyoutController;
@@ -222,7 +224,7 @@ public class BubbleBarViewController {
mIconSize = res.getDimensionPixelSize(R.dimen.bubblebar_icon_size);
mBubbleBarTaskbarMinDistance = res.getDimensionPixelSize(
R.dimen.bubblebar_transient_taskbar_min_distance);
- mDragElevation = res.getDimensionPixelSize(R.dimen.bubblebar_drag_elevation);
+ mDragElevation = res.getDimensionPixelSize(R.dimen.dragged_bubble_elevation);
mTaskbarTranslationDelta = getBubbleBarTranslationDeltaForTaskbar(activity);
if (DeviceConfig.isSmallTablet(mActivity)) {
mBubbleBarDropTargetSize = res.getDimensionPixelSize(R.dimen.drag_zone_bubble_fold);
@@ -364,7 +366,7 @@ public class BubbleBarViewController {
@Override
public boolean isOnLeft() {
boolean shouldRevertLocation =
- mBarView.isShowingDropTarget() && mIsLocationUpdatedForDropTarget;
+ mBarView.isShowingDropTarget() && isLocationUpdatedForDropTarget();
boolean isOnLeft = mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl());
return shouldRevertLocation != isOnLeft;
}
@@ -616,6 +618,17 @@ public class BubbleBarViewController {
mBarView.animateToBubbleBarLocation(bubbleBarLocation);
}
+ /** Return animator for animating bubble bar in. */
+ public Animator animateBubbleBarLocationIn(BubbleBarLocation fromLocation,
+ BubbleBarLocation toLocation) {
+ return mBarView.animateToBubbleBarLocationIn(fromLocation, toLocation);
+ }
+
+ /** Return animator for animating bubble bar out. */
+ public Animator animateBubbleBarLocationOut(BubbleBarLocation toLocation) {
+ return mBarView.animateToBubbleBarLocationOut(toLocation);
+ }
+
/** Returns whether the Bubble Bar is currently displaying a drop target. */
public boolean isShowingDropTarget() {
return mBarView.isShowingDropTarget();
@@ -628,25 +641,18 @@ public class BubbleBarViewController {
* updated.
*/
public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation bubbleBarLocation) {
- Log.w("BBAnimation", "onDragItemOverBubbleBarDragZone()");
+ mBubbleBarDragLocation = bubbleBarLocation;
mBarView.showDropTarget(/* isDropTarget = */ true);
- boolean isRtl = mBarView.isLayoutRtl();
- mIsLocationUpdatedForDropTarget = getBubbleBarLocation().isOnLeft(isRtl)
- != bubbleBarLocation.isOnLeft(isRtl);
mWasStashedBeforeEnteringBubbleDragZone = hasBubbles()
&& mBubbleStashController.isStashed();
if (mWasStashedBeforeEnteringBubbleDragZone) {
- if (mIsLocationUpdatedForDropTarget) {
- // bubble bar is stashed an location updated
- //TODO(b/399678274) add animation
- mBubbleStashController.showBubbleBarImmediate();
- animateBubbleBarLocation(bubbleBarLocation);
- } else {
- // bubble bar is stashed and location the same - just un-stash
- mBubbleStashController.showBubbleBar(/* expandBubbles = */ false);
- }
+ // bubble bar is stashed - un-stash at drag location
+ mBubbleStashController.showBubbleBarAtLocation(
+ /* fromLocation = */ getBubbleBarLocation(),
+ /* toLocation = */ mBubbleBarDragLocation
+ );
} else if (hasBubbles()) {
- if (mIsLocationUpdatedForDropTarget) {
+ if (isLocationUpdatedForDropTarget()) {
// bubble bar has bubbles and location is changed - animate bar to the opposite side
animateBubbleBarLocation(bubbleBarLocation);
}
@@ -661,7 +667,12 @@ public class BubbleBarViewController {
* {@link #onDragItemOverBubbleBarDragZone}}.
*/
public boolean isLocationUpdatedForDropTarget() {
- return mIsLocationUpdatedForDropTarget;
+ if (mBubbleBarDragLocation == null) {
+ return false;
+ }
+ boolean isRtl = mBarView.isLayoutRtl();
+ return getBubbleBarLocation().isOnLeft(isRtl)
+ != mBubbleBarDragLocation.isOnLeft(isRtl);
}
/**
@@ -671,39 +682,34 @@ public class BubbleBarViewController {
* mode and reset the value returned from {@link #isLocationUpdatedForDropTarget()} to false.
*/
public void onItemDraggedOutsideBubbleBarDropZone() {
- Log.w("BBAnimation", "onItemDraggedOutsideBubbleBarDropZone()");
- mBarView.showDropTarget(/* isDropTarget = */ false);
- if (mWasStashedBeforeEnteringBubbleDragZone) {
- if (mIsLocationUpdatedForDropTarget) {
- // bubble bar was stashed and location updated
- //TODO(b/399678274) add animation
- animateBubbleBarLocation(getBubbleBarLocation());
- mBubbleStashController.stashBubbleBarImmediate();
- } else {
- // bubble bar was stashed and location the same - just stash it back
- mBubbleStashController.stashBubbleBar();
- }
+ if (!isShowingDropTarget()) {
+ return;
+ }
+ if (mWasStashedBeforeEnteringBubbleDragZone && mBubbleBarDragLocation != null) {
+ // bubble bar was stashed - stash at original location
+ mBubbleStashController.stashBubbleBarToLocation(
+ /* fromLocation = */ mBubbleBarDragLocation,
+ /* toLocation = */ getBubbleBarLocation()
+ );
} else if (hasBubbles()) {
- if (mIsLocationUpdatedForDropTarget) {
- // bubble bar has bubbles and location was changed - return to the original location
+ if (isLocationUpdatedForDropTarget()) {
+ // bubble bar has bubbles and location was changed - return to the original
+ // location
animateBubbleBarLocation(getBubbleBarLocation());
}
}
- mBubbleBarPinController.hideDropTarget();
- mIsLocationUpdatedForDropTarget = false;
- mWasStashedBeforeEnteringBubbleDragZone = false;
+ onItemDragCompleted();
}
/**
* Notifies the controller that the drag has completed over the Bubble Bar drop zone.
* The controller will hide the drop target if there are no bubbles and exit drop target mode.
*/
- public void onItemDroppedInBubbleBarDragZone() {
- Log.w("BBAnimation", "onItemDroppedInBubbleBarDragZone()");
+ public void onItemDragCompleted() {
mBarView.showDropTarget(/* isDropTarget = */ false);
mBubbleBarPinController.hideDropTarget();
- mIsLocationUpdatedForDropTarget = false;
mWasStashedBeforeEnteringBubbleDragZone = false;
+ mBubbleBarDragLocation = null;
}
/**
@@ -842,6 +848,7 @@ public class BubbleBarViewController {
boolean hiddenForStashedAndNotAnimating =
mHiddenForStashed && !mBubbleBarViewAnimator.isAnimating();
if (mHiddenForSysui || mHiddenForNoBubbles || hiddenForStashedAndNotAnimating) {
+ //TODO(b/404870188) this visibility change cause search view drag misbehavior
mBarView.setVisibility(INVISIBLE);
} else {
mBarView.setVisibility(VISIBLE);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index 3640c3b60e..ec540e0088 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -272,6 +272,11 @@ public class BubbleStashedHandleViewController {
updateTranslationY();
}
+ /** Sets translation X for stash handle. */
+ public void setTranslationX(float translationX) {
+ mStashedHandleView.setTranslationX(translationX);
+ }
+
private void updateTranslationY() {
mStashedHandleView.setTranslationY(mTranslationForSwipeY + mTranslationForStashY);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
index 75bf937a87..ac87b5ee00 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
@@ -36,6 +36,8 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.animation.ArgbEvaluator
import com.android.launcher3.R
import com.android.launcher3.popup.RoundedArrowDrawable
+import com.android.wm.shell.shared.TypefaceUtils
+import com.android.wm.shell.shared.TypefaceUtils.Companion.setTypeface
import kotlin.math.min
/** The flyout view used to notify the user of a new bubble notification. */
@@ -163,6 +165,9 @@ class BubbleBarFlyoutView(
LayoutInflater.from(context).inflate(R.layout.bubblebar_flyout, this, true)
id = R.id.bubble_bar_flyout_view
+ setTypeface(title, TypefaceUtils.FontFamily.GSF_LABEL_LARGE)
+ setTypeface(message, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
+
val ta = context.obtainStyledAttributes(intArrayOf(android.R.attr.dialogCornerRadius))
cornerRadius = ta.getDimensionPixelSize(0, 0).toFloat()
ta.recycle()
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
index fec1eaf55c..ec272ac873 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
@@ -131,6 +131,12 @@ interface BubbleStashController {
*/
fun stashBubbleBar()
+ /**
+ * Animates the bubble bar to the handle at provided location. Does not update bubble bar
+ * location.
+ */
+ fun stashBubbleBarToLocation(fromLocation: BubbleBarLocation, toLocation: BubbleBarLocation) {}
+
/** Shows the bubble bar, and expands bubbles depending on [expandBubbles]. */
fun showBubbleBar(expandBubbles: Boolean) {
showBubbleBar(expandBubbles = expandBubbles, bubbleBarGesture = false)
@@ -144,6 +150,9 @@ interface BubbleStashController {
*/
fun showBubbleBar(expandBubbles: Boolean, bubbleBarGesture: Boolean)
+ /** Animates the bubble bar at the provided location. Does not update bubble bar location. */
+ fun showBubbleBarAtLocation(fromLocation: BubbleBarLocation, toLocation: BubbleBarLocation) {}
+
// TODO(b/354218264): Move to BubbleBarViewAnimator
/**
* The difference on the Y axis between the center of the handle and the center of the bubble
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
index 9c148e2191..82bb071bc3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -18,6 +18,7 @@ package com.android.launcher3.taskbar.bubbles.stashing
import android.animation.Animator
import android.animation.AnimatorSet
+import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Rect
import android.view.MotionEvent
@@ -31,6 +32,12 @@ import com.android.app.animation.Interpolators.LINEAR
import com.android.launcher3.R
import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.anim.SpringAnimationBuilder
+import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.FADE_IN_ANIM_ALPHA_DURATION_MS
+import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.FADE_OUT_ANIM_ALPHA_DELAY_MS
+import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.FADE_OUT_ANIM_ALPHA_DURATION_MS
+import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.FADE_OUT_ANIM_POSITION_DURATION_MS
+import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.inShiftX
+import com.android.launcher3.taskbar.BarsLocationAnimatorHelper.outShift
import com.android.launcher3.taskbar.TaskbarInsetsController
import com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_ALPHA_START_DELAY
import com.android.launcher3.taskbar.TaskbarStashController.TRANSIENT_TASKBAR_STASH_ALPHA_DURATION
@@ -44,6 +51,7 @@ import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Task
import com.android.launcher3.util.MultiPropertyFactory
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.ContextUtils.isRtl
import kotlin.math.max
class TransientBubbleStashController(
@@ -187,6 +195,11 @@ class TransientBubbleStashController(
}
override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) {
+ showBubbleBarImmediateVisually(bubbleBarTranslationY)
+ onIsStashedChanged()
+ }
+
+ private fun showBubbleBarImmediateVisually(bubbleBarTranslationY: Float) {
bubbleStashedHandleViewController?.setTranslationYForSwipe(0f)
stashHandleViewAlpha?.value = 0f
this.bubbleBarTranslationYAnimator.updateValue(bubbleBarTranslationY)
@@ -197,10 +210,14 @@ class TransientBubbleStashController(
bubbleBarBackgroundScaleY.updateValue(1f)
isStashed = false
bubbleBarViewController.setHiddenForStashed(false)
- onIsStashedChanged()
}
override fun stashBubbleBarImmediate() {
+ stashBubbleBarImmediateVisually()
+ onIsStashedChanged()
+ }
+
+ private fun stashBubbleBarImmediateVisually() {
bubbleStashedHandleViewController?.setTranslationYForSwipe(0f)
stashHandleViewAlpha?.value = 1f
this.bubbleBarTranslationYAnimator.updateValue(getStashTranslation())
@@ -212,7 +229,6 @@ class TransientBubbleStashController(
bubbleBarBackgroundScaleY.updateValue(getStashScaleY())
isStashed = true
bubbleBarViewController.setHiddenForStashed(true)
- onIsStashedChanged()
}
override fun getTouchableHeight(): Int =
@@ -247,6 +263,29 @@ class TransientBubbleStashController(
updateStashedAndExpandedState(stash = true, expand = false)
}
+ override fun stashBubbleBarToLocation(
+ fromLocation: BubbleBarLocation,
+ toLocation: BubbleBarLocation,
+ ) {
+ if (fromLocation.isSameSideWith(toLocation)) {
+ updateStashedAndExpandedState(
+ stash = true,
+ expand = false,
+ updateTouchRegionOnEnd = false,
+ )
+ return
+ }
+ cancelAnimation()
+ animator =
+ AnimatorSet().apply {
+ playSequentially(
+ bubbleBarViewController.animateBubbleBarLocationOut(toLocation),
+ createHandleInAnimator(location = toLocation),
+ )
+ start()
+ }
+ }
+
override fun showBubbleBar(expandBubbles: Boolean, bubbleBarGesture: Boolean) {
updateStashedAndExpandedState(
stash = false,
@@ -255,6 +294,33 @@ class TransientBubbleStashController(
)
}
+ override fun showBubbleBarAtLocation(
+ fromLocation: BubbleBarLocation,
+ toLocation: BubbleBarLocation,
+ ) {
+ if (fromLocation.isSameSideWith(toLocation)) {
+ updateStashedAndExpandedState(
+ stash = false,
+ expand = false,
+ updateTouchRegionOnEnd = false,
+ )
+ return
+ }
+ cancelAnimation()
+ val bubbleBarInAnimation =
+ bubbleBarViewController.animateBubbleBarLocationIn(fromLocation, toLocation).apply {
+ doOnStart { showBubbleBarImmediateVisually(bubbleBarTranslationY) }
+ }
+ animator =
+ AnimatorSet().apply {
+ playSequentially(
+ createHandleOutAnimator(location = toLocation),
+ bubbleBarInAnimation,
+ )
+ start()
+ }
+ }
+
override fun getDiffBetweenHandleAndBarCenters(): Float {
// the difference between the centers of the handle and the bubble bar is the difference
// between their distance from the bottom of the screen.
@@ -392,7 +458,7 @@ class TransientBubbleStashController(
bubbleBarAlpha.value = 1f
}
animatorSet.doOnEnd {
- animator = null
+ cancelAnimation()
controllersAfterInitAction.runAfterInit {
if (isStashed) {
bubbleBarAlpha.value = 0f
@@ -486,6 +552,7 @@ class TransientBubbleStashController(
stash: Boolean,
expand: Boolean,
bubbleBarGesture: Boolean = false,
+ updateTouchRegionOnEnd: Boolean = true,
) {
if (bubbleBarViewController.isHiddenForNoBubbles) {
// If there are no bubbles the bar and handle are invisible, nothing to do here.
@@ -498,11 +565,13 @@ class TransientBubbleStashController(
// notify the view controller that the stash state is about to change so that it can
// cancel an ongoing animation if there is one.
bubbleBarViewController.onStashStateChanging()
- animator?.cancel()
+ cancelAnimation()
animator =
createStashAnimator(isStashed, BAR_STASH_DURATION).apply {
updateBarVisibility(isStashed)
- updateTouchRegionOnAnimationEnd()
+ if (updateTouchRegionOnEnd) {
+ updateTouchRegionOnAnimationEnd()
+ }
start()
}
}
@@ -512,6 +581,11 @@ class TransientBubbleStashController(
}
}
+ private fun cancelAnimation() {
+ animator?.cancel()
+ animator = null
+ }
+
override fun getHandleViewAlpha(): MultiPropertyFactory<View>.MultiProperty? =
// only return handle alpha if the bubble bar is stashed and has bubbles
if (isStashed && bubbleBarViewController.hasBubbles()) {
@@ -534,6 +608,49 @@ class TransientBubbleStashController(
return this
}
+ // TODO(b/399678274) add tests
+ private fun createHandleInAnimator(location: BubbleBarLocation): Animator? {
+ val stashHandleViewController = bubbleStashedHandleViewController ?: return null
+ val handleAlpha = stashHandleViewController.stashedHandleAlpha.get(0)
+ val shift = context.inShiftX
+ val startX = if (location.isOnLeft(context.isRtl)) shift else -shift
+ val handleTranslationX =
+ ValueAnimator.ofFloat(startX, 0f).apply {
+ addUpdateListener { v ->
+ stashHandleViewController.setTranslationX(v.animatedValue as Float)
+ }
+ duration = FADE_IN_ANIM_ALPHA_DURATION_MS
+ }
+ val handleAlphaAnimation =
+ handleAlpha.animateToValue(1f).apply { duration = FADE_IN_ANIM_ALPHA_DURATION_MS }
+ return AnimatorSet().apply {
+ playTogether(handleTranslationX, handleAlphaAnimation)
+ doOnStart { stashBubbleBarImmediateVisually() }
+ }
+ }
+
+ private fun createHandleOutAnimator(location: BubbleBarLocation): Animator? {
+ val stashHandleViewController = bubbleStashedHandleViewController ?: return null
+ val handleAlpha = stashHandleViewController.stashedHandleAlpha.get(0)
+ val shift = context.outShift
+ val endX = if (location.isOnLeft(context.isRtl)) -shift else shift
+ val handleTranslationX =
+ ValueAnimator.ofFloat(0f, endX).apply {
+ addUpdateListener { v ->
+ stashHandleViewController.setTranslationX(v.animatedValue as Float)
+ }
+ duration = FADE_OUT_ANIM_POSITION_DURATION_MS
+ // in case item dropped to the opposite side - need to clear translation
+ doOnEnd { stashHandleViewController.setTranslationX(0f) }
+ }
+ val handleAlphaAnimation =
+ handleAlpha.animateToValue(0f).apply {
+ duration = FADE_OUT_ANIM_ALPHA_DURATION_MS
+ startDelay = FADE_OUT_ANIM_ALPHA_DELAY_MS
+ }
+ return AnimatorSet().apply { playTogether(handleTranslationX, handleAlphaAnimation) }
+ }
+
private fun Animator.setBubbleBarPivotDuringAnim(pivotX: Float, pivotY: Float): Animator {
var initialPivotX = Float.NaN
var initialPivotY = Float.NaN
@@ -549,4 +666,9 @@ class TransientBubbleStashController(
}
return this
}
+
+ private fun BubbleBarLocation.isSameSideWith(anotherLocation: BubbleBarLocation): Boolean {
+ val isRtl = context.isRtl
+ return this.isOnLeft(isRtl) == anotherLocation.isOnLeft(isRtl)
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index bb2acd6362..a1df21fa04 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -37,6 +37,7 @@ import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
import com.android.quickstep.DeviceConfigWrapper
import com.android.quickstep.util.ContextualSearchStateManager
+import com.android.wm.shell.Flags
/** Taskbar all apps button container for customizable taskbar. */
class TaskbarAllAppsButtonContainer
@@ -97,14 +98,34 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
@DrawableRes
private fun getAllAppsButton(isTransientTaskbar: Boolean): Int {
+ if (Flags.enableGsf()) {
+ return getAllAppsButtonForExpressiveTheme()
+ }
val shouldSelectTransientIcon =
isTransientTaskbar || (enableTaskbarPinning() && !activityContext.isThreeButtonNav)
return if (shouldSelectTransientIcon) R.drawable.ic_transient_taskbar_all_apps_search_button
else R.drawable.ic_taskbar_all_apps_search_button
}
+ @DrawableRes
+ private fun getAllAppsButtonForExpressiveTheme(): Int {
+ return R.drawable.ic_taskbar_all_apps_search_button_expressive_theme
+ }
+
+ @DimenRes
+ fun getAllAppsButtonTranslationXOffsetForExpressiveTheme(isTransientTaskbar: Boolean): Int {
+ return if (isTransientTaskbar) {
+ R.dimen.transient_taskbar_all_apps_button_translation_x_offset_for_expressive_theme
+ } else {
+ R.dimen.taskbar_all_apps_search_button_translation_x_offset_for_expressive_theme
+ }
+ }
+
@DimenRes
fun getAllAppsButtonTranslationXOffset(isTransientTaskbar: Boolean): Int {
+ if (Flags.enableGsf()) {
+ return getAllAppsButtonTranslationXOffsetForExpressiveTheme(isTransientTaskbar)
+ }
return if (isTransientTaskbar) {
R.dimen.transient_taskbar_all_apps_button_translation_x_offset
} else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
index 060ce46619..08a1b48acf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
@@ -20,6 +20,7 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color.TRANSPARENT
+import android.graphics.drawable.Drawable
import android.util.AttributeSet
import androidx.core.view.setPadding
import com.android.launcher3.R
@@ -28,6 +29,7 @@ import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarViewCallbacks
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
+import com.android.wm.shell.Flags
/** Taskbar divider view container for customizable taskbar. */
class TaskbarDividerContainer
@@ -46,16 +48,24 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
setUpIcon()
}
- @SuppressLint("UseCompatLoadingForDrawables")
fun setUpIcon() {
backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
- val drawable = resources.getDrawable(R.drawable.taskbar_divider_button)
+ val drawable = getTaskbarDividerIcon()
setIconDrawable(drawable)
if (!activityContext.isTransientTaskbar) {
setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
}
}
+ @SuppressLint("UseCompatLoadingForDrawables")
+ fun getTaskbarDividerIcon(): Drawable {
+ return if (Flags.enableGsf()) {
+ resources.getDrawable(R.drawable.taskbar_divider_button_expressive_theme)
+ } else {
+ resources.getDrawable(R.drawable.taskbar_divider_button)
+ }
+ }
+
@SuppressLint("ClickableViewAccessibility")
fun setUpCallbacks(callbacks: TaskbarViewCallbacks) {
setOnLongClickListener(callbacks.taskbarDividerLongClickListener)
diff --git a/quickstep/src/com/android/launcher3/taskbar/growth/ActionPerformers.kt b/quickstep/src/com/android/launcher3/taskbar/growth/ActionPerformers.kt
new file mode 100644
index 0000000000..17c950993d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/growth/ActionPerformers.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.growth
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+
+object ActionPerformers {
+ fun interface DismissCallback {
+ fun invoke()
+ }
+
+ fun performActions(actions: List<Action>, context: Context, dismissCallback: DismissCallback) {
+ for (action in actions) {
+ performAction(action, context, dismissCallback)
+ }
+ }
+
+ private fun performAction(action: Action, context: Context, dismissCallback: DismissCallback) {
+ when (action) {
+ is Action.Dismiss -> {
+ // TODO: b/396239267 - Handle marking the campaign dismissed with dismissal
+ // retention.
+ dismissCallback.invoke()
+ }
+ is Action.OpenUrl -> openUrl(action.url, context)
+ // Handle other actions
+ }
+ }
+
+ fun openUrl(url: String, context: Context) {
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ context.startActivity(intent)
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt b/quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt
new file mode 100644
index 0000000000..04e41bc3bd
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/growth/NudgeController.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar.growth
+
+import android.content.Context
+import com.android.launcher3.Utilities
+import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.launcher3.taskbar.TaskbarControllers
+import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
+import com.android.launcher3.util.DisplayController
+import com.android.launcher3.views.ActivityContext
+import java.io.PrintWriter
+
+/** Controls nudge lifecycles. */
+class NudgeController(context: Context) : LoggableTaskbarController {
+
+ protected val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
+
+ private val isNudgeEnabled: Boolean
+ get() {
+ return !Utilities.isRunningInTestHarness() &&
+ !activityContext.isPhoneMode &&
+ !activityContext.isTinyTaskbar
+ }
+
+ private lateinit var controllers: TaskbarControllers
+
+ fun init(controllers: TaskbarControllers) {
+ this.controllers = controllers
+ }
+
+ fun maybeShow(payload: NudgePayload) {
+ if (!isNudgeEnabled || !DisplayController.isTransientTaskbar(activityContext)) {
+ return
+ }
+ // TODO: b/398033012 - create and show nudge view based on the payload.
+ }
+
+ /** Closes the current [nudgeView]. */
+ fun hide() {
+ // TODO: b/398033012 - hide the nudge view.
+ }
+
+ override fun dumpLogs(prefix: String?, pw: PrintWriter?) {
+ pw?.println(prefix + "NudgeController:")
+ pw?.println("$prefix\tisNudgeEnabled=$isNudgeEnabled")
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
index e487f9fd40..3712a76eab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -27,7 +27,6 @@ import android.widget.Space
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
import com.android.launcher3.Utilities
-import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
/**
@@ -48,7 +47,7 @@ abstract class AbstractNavButtonLayoutter(
protected val startContextualContainer: ViewGroup,
protected val imeSwitcher: ImageView?,
protected val a11yButton: ImageView?,
- protected val space: Space?
+ protected val space: Space?,
) : NavButtonLayoutter {
protected val homeButton: ImageView? = navButtonContainer.findViewById(R.id.home)
protected val recentsButton: ImageView? = navButtonContainer.findViewById(R.id.recent_apps)
@@ -69,26 +68,34 @@ abstract class AbstractNavButtonLayoutter(
val params =
FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
+ ViewGroup.LayoutParams.MATCH_PARENT,
)
params.gravity = Gravity.CENTER
return params
}
+ /**
+ * Adjusts the layout parameters of the nav bar container for setup in phone mode.
+ *
+ * @param nearestTouchFrameLayoutParams The layout parameters of the navButtonsView, which is
+ * the ViewGroup that contains start, end, nav button ViewGroups
+ * @param deviceProfile The device profile containing information about the device's
+ * configuration.
+ */
fun adjustForSetupInPhoneMode(
- navButtonsLayoutParams: FrameLayout.LayoutParams,
- navButtonsViewLayoutParams: FrameLayout.LayoutParams,
- deviceProfile: DeviceProfile
+ nearestTouchFrameLayoutParams: FrameLayout.LayoutParams,
+ deviceProfile: DeviceProfile,
) {
val phoneOrPortraitSetupMargin =
resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_margin)
- navButtonsLayoutParams.marginStart = phoneOrPortraitSetupMargin
- navButtonsLayoutParams.bottomMargin =
+ nearestTouchFrameLayoutParams.marginStart = phoneOrPortraitSetupMargin
+ nearestTouchFrameLayoutParams.bottomMargin =
if (!deviceProfile.isLandscape) 0
else
phoneOrPortraitSetupMargin -
- resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2
- navButtonsViewLayoutParams.height =
+ resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2
+
+ nearestTouchFrameLayoutParams.height =
resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_height)
}
@@ -97,7 +104,7 @@ abstract class AbstractNavButtonLayoutter(
buttonSize: Int,
barAxisMarginStart: Int,
barAxisMarginEnd: Int,
- gravity: Int
+ gravity: Int,
) {
val contextualContainerParams =
FrameLayout.LayoutParams(buttonSize, ViewGroup.LayoutParams.MATCH_PARENT)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
index 2497fbb98e..a199dba0bd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -66,7 +66,7 @@ class NavButtonLayoutFactory {
isInSetup: Boolean,
isThreeButtonNav: Boolean,
phoneMode: Boolean,
- @Rotation surfaceRotation: Int
+ @Rotation surfaceRotation: Int,
): NavButtonLayoutter {
val navButtonContainer =
navButtonsView.requireViewById<LinearLayout>(ID_END_NAV_BUTTONS)
@@ -77,6 +77,18 @@ class NavButtonLayoutFactory {
val isPhoneNavMode = phoneMode && isThreeButtonNav
val isPhoneGestureMode = phoneMode && !isThreeButtonNav
return when {
+ isInSetup -> {
+ SetupNavLayoutter(
+ resources,
+ navButtonsView,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ a11yButton,
+ space,
+ )
+ }
isPhoneNavMode -> {
if (!deviceProfile.isLandscape) {
navButtonsView.setIsVertical(false)
@@ -87,7 +99,7 @@ class NavButtonLayoutFactory {
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
} else if (surfaceRotation == ROTATION_90) {
navButtonsView.setIsVertical(true)
@@ -98,7 +110,7 @@ class NavButtonLayoutFactory {
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
} else {
navButtonsView.setIsVertical(true)
@@ -109,36 +121,23 @@ class NavButtonLayoutFactory {
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
}
}
isPhoneGestureMode -> {
PhoneGestureLayoutter(
resources,
- navButtonsView,
navButtonContainer,
endContextualContainer,
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
}
deviceProfile.isTaskbarPresent -> {
return when {
- isInSetup -> {
- SetupNavLayoutter(
- resources,
- navButtonsView,
- navButtonContainer,
- endContextualContainer,
- startContextualContainer,
- imeSwitcher,
- a11yButton,
- space
- )
- }
isKidsMode -> {
KidsNavLayoutter(
resources,
@@ -147,7 +146,7 @@ class NavButtonLayoutFactory {
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
}
else ->
@@ -158,7 +157,7 @@ class NavButtonLayoutFactory {
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
index 390ec342e1..e0f2a22b2c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
@@ -17,25 +17,21 @@
package com.android.launcher3.taskbar.navbutton
import android.content.res.Resources
-import android.view.Gravity
import android.view.ViewGroup
-import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Space
-import com.android.launcher3.DeviceProfile
import com.android.launcher3.taskbar.TaskbarActivityContext
/** Layoutter for showing gesture navigation on phone screen. No buttons here, no-op container */
class PhoneGestureLayoutter(
resources: Resources,
- navButtonsView: NearestTouchFrame,
navBarContainer: LinearLayout,
endContextualContainer: ViewGroup,
startContextualContainer: ViewGroup,
imeSwitcher: ImageView?,
a11yButton: ImageView?,
- space: Space?
+ space: Space?,
) :
AbstractNavButtonLayoutter(
resources,
@@ -44,33 +40,10 @@ class PhoneGestureLayoutter(
startContextualContainer,
imeSwitcher,
a11yButton,
- space
+ space,
) {
- private val mNavButtonsView = navButtonsView
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
- // TODO: look into if we should use SetupNavLayoutter instead.
- if (!context.isUserSetupComplete) {
- // Since setup wizard only has back button enabled, it looks strange to be
- // end-aligned, so start-align instead.
- val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
- val navButtonsViewLayoutParams =
- mNavButtonsView.layoutParams as FrameLayout.LayoutParams
- val deviceProfile: DeviceProfile = context.deviceProfile
-
- navButtonsLayoutParams.marginEnd = 0
- navButtonsLayoutParams.gravity = Gravity.START
- context.setTaskbarWindowSize(context.setupWindowSize)
-
- adjustForSetupInPhoneMode(
- navButtonsLayoutParams,
- navButtonsViewLayoutParams,
- deviceProfile
- )
- mNavButtonsView.layoutParams = navButtonsViewLayoutParams
- navButtonContainer.layoutParams = navButtonsLayoutParams
- }
-
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index e032430c1d..eb3fdeb99e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -29,12 +29,15 @@ import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarActivityContext
+const val SUW_THEME_SYSTEM_PROPERTY = "setupwizard.theme"
+const val GLIF_EXPRESSIVE_THEME = "glif_expressive"
+const val GLIF_EXPRESSIVE_LIGHT_THEME = "glif_expressive_light"
const val SQUARE_ASPECT_RATIO_BOTTOM_BOUND = 0.95
const val SQUARE_ASPECT_RATIO_UPPER_BOUND = 1.05
class SetupNavLayoutter(
resources: Resources,
- navButtonsView: NearestTouchFrame,
+ nearestTouchFrame: NearestTouchFrame,
navButtonContainer: LinearLayout,
endContextualContainer: ViewGroup,
startContextualContainer: ViewGroup,
@@ -51,17 +54,19 @@ class SetupNavLayoutter(
a11yButton,
space,
) {
- private val mNavButtonsView = navButtonsView
+ // mNearestTouchFrame is a ViewGroup that contains start, end, nav button ViewGroups
+ private val mNearestTouchFrame = nearestTouchFrame
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
- val SUWTheme = SystemProperties.get("setupwizard.theme", "")
- if (SUWTheme == "glif_expressive" || SUWTheme == "glif_expressive_light") {
+ val SUWTheme = SystemProperties.get(SUW_THEME_SYSTEM_PROPERTY, "")
+ if (SUWTheme == GLIF_EXPRESSIVE_THEME || SUWTheme == GLIF_EXPRESSIVE_LIGHT_THEME) {
return
}
// Since setup wizard only has back button enabled, it looks strange to be
// end-aligned, so start-align instead.
val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
- val navButtonsViewLayoutParams = mNavButtonsView.layoutParams as FrameLayout.LayoutParams
+ val navButtonsOverallViewGroupLayoutParams =
+ mNearestTouchFrame.layoutParams as FrameLayout.LayoutParams
val deviceProfile: DeviceProfile = context.deviceProfile
navButtonsLayoutParams.marginEnd = 0
@@ -77,18 +82,14 @@ class SetupNavLayoutter(
) {
navButtonsLayoutParams.marginStart =
resources.getDimensionPixelSize(R.dimen.taskbar_back_button_suw_start_margin)
- navButtonsViewLayoutParams.bottomMargin =
+ navButtonsOverallViewGroupLayoutParams.bottomMargin =
resources.getDimensionPixelSize(R.dimen.taskbar_back_button_suw_bottom_margin)
navButtonsLayoutParams.height =
resources.getDimensionPixelSize(R.dimen.taskbar_back_button_suw_height)
} else {
- adjustForSetupInPhoneMode(
- navButtonsLayoutParams,
- navButtonsViewLayoutParams,
- deviceProfile,
- )
+ adjustForSetupInPhoneMode(navButtonsOverallViewGroupLayoutParams, deviceProfile)
}
- mNavButtonsView.layoutParams = navButtonsViewLayoutParams
+ mNearestTouchFrame.layoutParams = navButtonsOverallViewGroupLayoutParams
navButtonContainer.layoutParams = navButtonsLayoutParams
endContextualContainer.removeAllViews()
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index dd91d17c29..8574b89ecb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -31,6 +31,7 @@ import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView;
import com.android.launcher3.taskbar.allapps.TaskbarSearchSessionController;
+import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
/**
@@ -142,6 +143,41 @@ public class TaskbarOverlayContext extends BaseTaskbarContext {
}
@Override
+ public boolean isTransientTaskbar() {
+ return mTaskbarContext.isTransientTaskbar();
+ }
+
+ @Override
+ public boolean isPinnedTaskbar() {
+ return mTaskbarContext.isPinnedTaskbar();
+ }
+
+ @Override
+ public NavigationMode getNavigationMode() {
+ return mTaskbarContext.getNavigationMode();
+ }
+
+ @Override
+ public boolean isInDesktopMode() {
+ return mTaskbarContext.isInDesktopMode();
+ }
+
+ @Override
+ public boolean showLockedTaskbarOnHome() {
+ return mTaskbarContext.showLockedTaskbarOnHome();
+ }
+
+ @Override
+ public boolean showDesktopTaskbarForFreeformDisplay() {
+ return mTaskbarContext.showDesktopTaskbarForFreeformDisplay();
+ }
+
+ @Override
+ public boolean isPrimaryDisplay() {
+ return mTaskbarContext.isPrimaryDisplay();
+ }
+
+ @Override
public void onDragStart() {}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index 675d55b9a4..de20f779a7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -200,7 +200,7 @@ public final class TaskbarOverlayController {
private LayoutParams createLayoutParams() {
LayoutParams layoutParams = new LayoutParams(
TYPE_APPLICATION_OVERLAY,
- LayoutParams.FLAG_SPLIT_TOUCH,
+ /* flags = */ 0,
PixelFormat.TRANSLUCENT);
layoutParams.setTitle(WINDOW_TITLE);
layoutParams.gravity = Gravity.BOTTOM;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 15a27d15c0..e8de0d2152 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -402,16 +402,16 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
canvas.scale(
mRingScale * (1f - RING_EFFECT_RATIO),
mRingScale * (1f - RING_EFFECT_RATIO),
- canvas.getWidth() / 2f,
- canvas.getHeight() / 2f);
+ getWidth() / 2f,
+ getHeight() / 2f);
} else if (Float.compare(1, mRingScale) != 0) {
- canvas.scale(mRingScale, mRingScale, canvas.getWidth() / 2f, canvas.getHeight() / 2f);
+ canvas.scale(mRingScale, mRingScale, getWidth() / 2f, getHeight() / 2f);
}
// Draw ring shadow around canvas.
canvas.drawPath(mRingPath, mIconRingPaint);
mIconRingPaint.setColor(mPlateColor.currentColor);
if (Flags.enableLauncherIconShapes()) {
- mIconRingPaint.setStrokeWidth(canvas.getWidth() * RING_EFFECT_RATIO);
+ mIconRingPaint.setStrokeWidth(getWidth() * RING_EFFECT_RATIO);
// Using FILL_AND_STROKE as there is still some gap to fill,
// between inner curve of ring / outer curve of icon.
mIconRingPaint.setStyle(Paint.Style.FILL_AND_STROKE);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java
deleted file mode 100644
index 45813ce52e..0000000000
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHost.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides;
-
-import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
-
-import android.appwidget.AppWidgetHost;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.Context;
-import android.os.Looper;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.util.Executors;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.widget.LauncherWidgetHolder;
-
-import java.util.function.IntConsumer;
-
-/**
- * {@link AppWidgetHost} that is used to receive the changes to the widgets without
- * storing any {@code Activity} info like that of the launcher.
- */
-final class QuickstepAppWidgetHost extends AppWidgetHost {
- private final @NonNull Context mContext;
- private final @NonNull IntConsumer mAppWidgetRemovedCallback;
- private final @NonNull LauncherWidgetHolder.ProviderChangedListener mProvidersChangedListener;
-
- QuickstepAppWidgetHost(@NonNull Context context, @NonNull IntConsumer appWidgetRemovedCallback,
- @NonNull LauncherWidgetHolder.ProviderChangedListener listener,
- @NonNull Looper looper) {
- super(context, APPWIDGET_HOST_ID, null, looper);
- mContext = context;
- mAppWidgetRemovedCallback = appWidgetRemovedCallback;
- mProvidersChangedListener = listener;
- }
-
- @Override
- protected void onProvidersChanged() {
- mProvidersChangedListener.notifyWidgetProvidersChanged();
- }
-
- @Override
- public void onAppWidgetRemoved(int appWidgetId) {
- // Route the call via model thread, in case it comes while a loader-bind is in progress
- Executors.MODEL_EXECUTOR.execute(
- () -> Executors.MAIN_EXECUTOR.execute(
- () -> mAppWidgetRemovedCallback.accept(appWidgetId)));
- }
-
- @Override
- protected void onProviderChanged(int appWidgetId, @NonNull AppWidgetProviderInfo appWidget) {
- LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
- mContext, appWidget);
- super.onProviderChanged(appWidgetId, info);
- // The super method updates the dimensions of the providerInfo. Update the
- // launcher spans accordingly.
- info.initSpans(mContext, LauncherAppState.getIDP(mContext));
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHostProvider.kt b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHostProvider.kt
new file mode 100644
index 0000000000..1387cb7fce
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepAppWidgetHostProvider.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides
+
+import android.app.ActivityThread
+import android.content.Context
+import android.content.ContextWrapper
+import com.android.launcher3.BuildConfig
+import com.android.launcher3.util.LooperExecutor
+import com.android.launcher3.widget.LauncherWidgetHolder
+import com.android.launcher3.widget.ListenableAppWidgetHost
+
+object QuickstepAppWidgetHostProvider {
+
+ /** Static widget host which is always listening and is lazily created */
+ @JvmStatic
+ val staticQuickstepHost: ListenableAppWidgetHost by lazy {
+ ListenableAppWidgetHost(
+ LooperContext(
+ ActivityThread.currentApplication(),
+ ListenableAppWidgetHost.widgetHolderExecutor,
+ ),
+ LauncherWidgetHolder.APPWIDGET_HOST_ID,
+ )
+ .apply { if (BuildConfig.WIDGETS_ENABLED) startListening() }
+ }
+
+ private class LooperContext(ctx: Context, val executor: LooperExecutor) : ContextWrapper(ctx) {
+
+ override fun getMainLooper() = executor.looper
+
+ override fun getMainExecutor() = executor
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 26a1322ea6..2f61eab845 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -34,8 +34,11 @@ import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import java.util.function.Consumer;
+
/** Provides a Quickstep specific animation when launching an activity from an app widget. */
-class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
+class QuickstepInteractionHandler implements RemoteViews.InteractionHandler,
+ Consumer<LauncherAppWidgetHostView> {
private static final String TAG = "QuickstepInteractionHandler";
@@ -45,6 +48,11 @@ class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
mLauncher = launcher;
}
+ @Override
+ public void accept(LauncherAppWidgetHostView host) {
+ host.setInteractionHandler(this);
+ }
+
@SuppressWarnings("NewApi")
@Override
public boolean onInteraction(View view, PendingIntent pendingIntent,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index cd0a4f312c..605fd312d0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -86,6 +86,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -142,7 +143,6 @@ import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepHolderFactory;
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
@@ -171,7 +171,6 @@ import com.android.launcher3.util.StableViewInfo;
import com.android.launcher3.util.StartActivityParams;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.FloatingIconView;
-import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.OverviewComponentObserver;
import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
@@ -226,6 +225,7 @@ import java.util.stream.Stream;
public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
SystemShortcut.BubbleActivityStarter {
+ private static final String TAG = "QuickstepLauncher";
private static final boolean TRACE_LAYOUTS =
SystemProperties.getBoolean("persist.debug.trace_layouts", false);
private static final String TRACE_RELAYOUT_CLASS =
@@ -296,6 +296,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
@Override
protected void setupViews() {
+ getAppWidgetHolder().setOnViewCreationCallback(new QuickstepInteractionHandler(this));
super.setupViews();
mActionsView = findViewById(R.id.overview_actions_view);
@@ -561,20 +562,35 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
@Override
public void onDestroy() {
+ // wrap non-trivial clean up blocks in try-catch to avoid stopping clean up of rest of
+ // objects
+
if (mAppTransitionManager != null) {
- mAppTransitionManager.onActivityDestroyed();
+ try {
+ mAppTransitionManager.onActivityDestroyed();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to destroy mAppTransitionManager", e);
+ }
}
mAppTransitionManager = null;
mIsPredictiveBackToHomeInProgress = false;
if (mUnfoldTransitionProgressProvider != null) {
- SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
- mUnfoldTransitionProgressProvider.destroy();
+ try {
+ SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
+ mUnfoldTransitionProgressProvider.destroy();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to destroy mUnfoldTransitionProgressProvider", e);
+ }
}
OverviewComponentObserver.INSTANCE.get(this)
.removeOverviewChangeListener(mOverviewChangeListener);
- mTISBindHelper.onDestroy();
+ try {
+ mTISBindHelper.onDestroy();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to destroy mTISBindHelper", e);
+ }
if (mLauncherUnfoldAnimationController != null) {
mLauncherUnfoldAnimationController.onDestroy();
@@ -584,15 +600,22 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
mSplitSelectStateController.onDestroy();
}
- RecentsView recentsView = getOverviewPanel();
- if (recentsView != null) {
- recentsView.destroy();
+ try {
+ RecentsView recentsView = getOverviewPanel();
+ if (recentsView != null) {
+ recentsView.destroy();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to destroy RecentsView", e);
}
- super.onDestroy();
- mHotseatPredictionController.destroy();
- if (mViewCapture != null) mViewCapture.close();
- removeBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
+ try {
+ super.onDestroy();
+ } finally { // trivial close operations in finally.
+ mHotseatPredictionController.destroy();
+ if (mViewCapture != null) mViewCapture.close();
+ removeBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
+ }
}
@Override
@@ -702,15 +725,6 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer,
}
@Override
- protected LauncherWidgetHolder createAppWidgetHolder() {
- final QuickstepHolderFactory factory =
- (QuickstepHolderFactory) LauncherWidgetHolder.HolderFactory.newFactory(this);
- return factory.newInstance(this,
- appWidgetId -> getWorkspace().removeWidget(appWidgetId),
- new QuickstepInteractionHandler(this));
- }
-
- @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
index 56fc4d1c0b..c7eedb0a1d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -16,43 +16,41 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
+import static com.android.launcher3.uioverrides.QuickstepAppWidgetHostProvider.getStaticQuickstepHost;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.widget.ListenableAppWidgetHost.getWidgetHolderExecutor;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
-import android.util.Log;
import android.util.SparseArray;
import android.widget.RemoteViews;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.LauncherWidgetHolder;
-import java.util.ArrayList;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.util.Collections;
-import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.BiConsumer;
-import java.util.function.IntConsumer;
/**
* {@link LauncherWidgetHolder} that puts the app widget host in the background
*/
public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
- private static final String TAG = "QuickstepWidgetHolder";
-
private static final UpdateKey<AppWidgetProviderInfo> KEY_PROVIDER_UPDATE =
AppWidgetHostView::onUpdateProviderInfo;
private static final UpdateKey<RemoteViews> KEY_VIEWS_UPDATE =
@@ -60,51 +58,17 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
private static final UpdateKey<Integer> KEY_VIEW_DATA_CHANGED =
AppWidgetHostView::onViewDataChanged;
- private static final List<QuickstepWidgetHolder> sHolders = new ArrayList<>();
private static final SparseArray<QuickstepWidgetHolderListener> sListeners =
new SparseArray<>();
- private static AppWidgetHost sWidgetHost = null;
-
private final UpdateHandler mUpdateHandler = this::onWidgetUpdate;
- private final @Nullable RemoteViews.InteractionHandler mInteractionHandler;
-
- private final @NonNull IntConsumer mAppWidgetRemovedCallback;
// Map to all pending updated keyed with appWidgetId;
private final SparseArray<PendingUpdate> mPendingUpdateMap = new SparseArray<>();
- private QuickstepWidgetHolder(@NonNull Context context,
- @Nullable IntConsumer appWidgetRemovedCallback,
- @Nullable RemoteViews.InteractionHandler interactionHandler) {
- super(context, appWidgetRemovedCallback);
- mAppWidgetRemovedCallback = appWidgetRemovedCallback != null ? appWidgetRemovedCallback
- : i -> {};
- mInteractionHandler = interactionHandler;
- MAIN_EXECUTOR.execute(() -> sHolders.add(this));
- }
-
- @Override
- @NonNull
- protected AppWidgetHost createHost(@NonNull Context context,
- @Nullable IntConsumer appWidgetRemovedCallback) {
- if (sWidgetHost == null) {
- sWidgetHost = new QuickstepAppWidgetHost(context.getApplicationContext(),
- i -> MAIN_EXECUTOR.execute(() ->
- sHolders.forEach(h -> h.mAppWidgetRemovedCallback.accept(i))),
- () -> MAIN_EXECUTOR.execute(() ->
- sHolders.forEach(h ->
- // Listeners might remove themselves from the list during the
- // iteration. Creating a copy of the list to avoid exceptions
- // for concurrent modification.
- new ArrayList<>(h.mProviderChangedListeners).forEach(
- ProviderChangedListener::notifyWidgetProvidersChanged))),
- getWidgetHolderExecutor().getLooper());
- if (WIDGETS_ENABLED) {
- sWidgetHost.startListening();
- }
- }
- return sWidgetHost;
+ @AssistedInject
+ public QuickstepWidgetHolder(@Assisted("UI_CONTEXT") @NonNull Context context) {
+ super(context, getStaticQuickstepHost());
}
@Override
@@ -168,21 +132,6 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
sListeners.remove(appWidgetId);
}
- /**
- * Called when the launcher is destroyed
- */
- @Override
- public void destroy() {
- try {
- MAIN_EXECUTOR.submit(() -> {
- clearViews();
- sHolders.remove(this);
- }).get();
- } catch (Exception e) {
- Log.e(TAG, "Failed to remove self from holder list", e);
- }
- }
-
@Override
protected boolean shouldListen(int flags) {
return (flags & (FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED))
@@ -199,7 +148,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
}
getWidgetHolderExecutor().execute(() -> {
- sWidgetHost.setAppWidgetHidden();
+ mWidgetHost.setAppWidgetHidden();
setListeningFlag(false);
});
}
@@ -217,7 +166,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
};
QuickstepWidgetHolderListener holderListener = getHolderListener(appWidgetId);
holderListener.addHolder(handler);
- return () -> holderListener.mListeningHolders.remove(handler);
+ return () -> holderListener.removeHolder(handler);
}
/**
@@ -239,7 +188,6 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
protected LauncherAppWidgetHostView createViewInternal(
int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) {
LauncherAppWidgetHostView widgetView = new LauncherAppWidgetHostView(mContext);
- widgetView.setInteractionHandler(mInteractionHandler);
widgetView.setAppWidget(appWidgetId, appWidget);
widgetView.updateAppWidget(getHolderListener(appWidgetId).addHolder(mUpdateHandler));
return widgetView;
@@ -249,7 +197,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
QuickstepWidgetHolderListener listener = sListeners.get(appWidgetId);
if (listener == null) {
listener = new QuickstepWidgetHolderListener(appWidgetId);
- sWidgetHost.setListener(appWidgetId, listener);
+ getStaticQuickstepHost().setListener(appWidgetId, listener);
sListeners.put(appWidgetId, listener);
}
return listener;
@@ -262,7 +210,7 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
public void clearViews() {
mViews.clear();
for (int i = sListeners.size() - 1; i >= 0; i--) {
- sListeners.valueAt(i).mListeningHolders.remove(mUpdateHandler);
+ sListeners.valueAt(i).removeHolder(mUpdateHandler);
}
}
@@ -289,13 +237,15 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
mWidgetId = widgetId;
}
- @UiThread
- @Nullable
public RemoteViews addHolder(@NonNull UpdateHandler holder) {
- mListeningHolders.add(holder);
+ MAIN_EXECUTOR.execute(() -> mListeningHolders.add(holder));
return mRemoteViews;
}
+ public void removeHolder(@NonNull UpdateHandler holder) {
+ MAIN_EXECUTOR.execute(() -> mListeningHolders.remove(holder));
+ }
+
@Override
@AnyThread
public void onUpdateProviderInfo(@Nullable AppWidgetProviderInfo info) {
@@ -322,44 +272,13 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
}
}
- /**
- * {@code HolderFactory} subclass that takes an interaction handler as one of the parameters
- * when creating a new instance.
- */
- public static class QuickstepHolderFactory extends HolderFactory {
- @SuppressWarnings("unused")
- public QuickstepHolderFactory(Context context) { }
+ /** A factory that generates new instances of {@code LauncherWidgetHolder} */
+ @AssistedFactory
+ public interface QuickstepWidgetHolderFactory extends WidgetHolderFactory {
@Override
- public LauncherWidgetHolder newInstance(@NonNull Context context,
- @Nullable IntConsumer appWidgetRemovedCallback) {
- return newInstance(context, appWidgetRemovedCallback, null);
- }
-
- /**
- * @param context The context of the caller
- * @param appWidgetRemovedCallback The callback that is called when widgets are removed
- * @param interactionHandler The interaction handler when the widgets are clicked
- * @return A new {@link LauncherWidgetHolder} instance
- */
- public LauncherWidgetHolder newInstance(@NonNull Context context,
- @Nullable IntConsumer appWidgetRemovedCallback,
- @Nullable RemoteViews.InteractionHandler interactionHandler) {
-
- if (!FeatureFlags.ENABLE_WIDGET_HOST_IN_BACKGROUND.get()) {
- return new LauncherWidgetHolder(context, appWidgetRemovedCallback) {
- @Override
- protected AppWidgetHost createHost(Context context,
- @Nullable IntConsumer appWidgetRemovedCallback) {
- AppWidgetHost host = super.createHost(context, appWidgetRemovedCallback);
- host.setInteractionHandler(interactionHandler);
- return host;
- }
- };
- }
- return new QuickstepWidgetHolder(context, appWidgetRemovedCallback, interactionHandler);
- }
+ QuickstepWidgetHolder newInstance(@Assisted("UI_CONTEXT") @NonNull Context context);
}
private interface UpdateKey<T> extends BiConsumer<AppWidgetHostView, T> { }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
index 23dc81d4e6..c9f791c6ee 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.kt
@@ -21,6 +21,7 @@ import com.android.app.animation.Interpolators.FINAL_FRAME
import com.android.app.animation.Interpolators.INSTANT
import com.android.app.animation.Interpolators.LINEAR
import com.android.launcher3.Flags.enableDesktopExplodedView
+import com.android.launcher3.Flags.enableGridOnlyOverview
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
import com.android.launcher3.LauncherState
import com.android.launcher3.anim.AnimatedFloat
@@ -143,7 +144,11 @@ class RecentsViewStateController(private val launcher: QuickstepLauncher) :
recentsView,
TASK_MODALNESS,
toState.overviewModalness,
- config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR),
+ config.getInterpolator(
+ ANIM_OVERVIEW_MODAL,
+ if (enableGridOnlyOverview() && !toState.isRecentsViewVisible) FINAL_FRAME
+ else LINEAR,
+ ),
)
val fromState = launcher.stateManager.state
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 0c0b4fd203..ae82f82c01 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -15,11 +15,11 @@
*/
package com.android.launcher3.uioverrides.states;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import android.graphics.Rect;
-import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.views.ActivityContext;
@@ -50,6 +50,9 @@ public class OverviewModalTaskState extends OverviewState {
@Override
public float[] getOverviewScaleAndOffset(Launcher launcher) {
+ if (enableGridOnlyOverview()) {
+ return super.getOverviewScaleAndOffset(launcher);
+ }
return getOverviewScaleAndOffsetForModalState(launcher.getOverviewPanel());
}
@@ -65,7 +68,7 @@ public class OverviewModalTaskState extends OverviewState {
@Override
public boolean isTaskbarStashed(Launcher launcher) {
- if (Flags.enableGridOnlyOverview()) {
+ if (enableGridOnlyOverview()) {
return true;
}
return super.isTaskbarStashed(launcher);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 963504f761..3170df9571 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -17,10 +17,12 @@ package com.android.launcher3.uioverrides.states;
import static com.android.app.animation.Interpolators.DECELERATE_2;
import static com.android.launcher3.Flags.enableDesktopExplodedView;
+import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Rect;
import android.os.SystemProperties;
@@ -35,6 +37,7 @@ import com.android.quickstep.util.BaseDepthController;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.BlurUtils;
/**
* Definition for overview state
@@ -158,7 +161,9 @@ public class OverviewState extends LauncherState {
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
- return Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
+ return enableOverviewBackgroundWallpaperBlur() && BlurUtils.supportsBlursOnWindows()
+ ? Color.TRANSPARENT
+ : Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
index 7d823dc47a..06e6734a37 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewDismissTouchController.kt
@@ -64,6 +64,7 @@ CONTAINER : RecentsViewContainer {
private var hasDismissThresholdHapticRun = false
private var initialDisplacement: Float = 0f
private var recentsScaleAnimation: SpringAnimation? = null
+ private var isBlockedDuringDismissal = false
private fun canInterceptTouch(ev: MotionEvent): Boolean =
when {
@@ -148,6 +149,7 @@ CONTAINER : RecentsViewContainer {
}
override fun onDragStart(start: Boolean, startDisplacement: Float) {
+ if (isBlockedDuringDismissal) return
val taskBeingDragged = taskBeingDragged ?: return
debugLog(TAG, "Handling touch event.")
@@ -161,6 +163,7 @@ CONTAINER : RecentsViewContainer {
}
override fun onDrag(displacement: Float): Boolean {
+ if (isBlockedDuringDismissal) return true
val taskBeingDragged = taskBeingDragged ?: return false
val currentDisplacement = displacement + initialDisplacement
val boundedDisplacement =
@@ -216,14 +219,11 @@ CONTAINER : RecentsViewContainer {
}
override fun onDragEnd(velocity: Float) {
+ if (isBlockedDuringDismissal) return
val taskBeingDragged = taskBeingDragged ?: return
val currentDisplacement =
taskBeingDragged.secondaryDismissTranslationProperty.get(taskBeingDragged)
- if (currentDisplacement == 0f) {
- clearState()
- return
- }
val isBeyondDismissThreshold =
abs(currentDisplacement) > abs(DISMISS_THRESHOLD_FRACTION * dismissLength)
val velocityIsGoingUp = recentsView.pagedOrientationHandler.isGoingUp(velocity, isRtl)
@@ -237,7 +237,6 @@ CONTAINER : RecentsViewContainer {
taskBeingDragged,
velocity,
isDismissing,
- detector,
dismissLength,
this::clearState,
)
@@ -246,6 +245,7 @@ CONTAINER : RecentsViewContainer {
if (isDismissing) (dismissLength * verticalFactor).toFloat() else 0f
)
}
+ isBlockedDuringDismissal = true
recentsScaleAnimation =
recentsView.animateRecentsScale(RECENTS_SCALE_DEFAULT).addEndListener { _, _, _, _ ->
recentsScaleAnimation = null
@@ -258,6 +258,7 @@ CONTAINER : RecentsViewContainer {
taskBeingDragged?.translationZ = 0f
taskBeingDragged = null
springAnimation = null
+ isBlockedDuringDismissal = false
}
private fun getRecentsScale(dismissFraction: Float): Float {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 168b856b25..48630b11d3 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -161,7 +161,6 @@ import com.android.quickstep.views.TaskView;
import com.android.quickstep.views.TaskViewType;
import com.android.systemui.animation.TransitionAnimator;
import com.android.systemui.contextualeducation.GestureType;
-import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
@@ -170,6 +169,7 @@ import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.Flags;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.startingsurface.SplashScreenExitAnimationUtils;
@@ -182,7 +182,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
-import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.Consumer;
@@ -693,26 +692,23 @@ public abstract class AbsSwipeUpHandler<
}
protected void notifyGestureAnimationStartToRecents() {
- Task[] runningTasks;
- TopTaskTracker.CachedTaskInfo cachedTaskInfo = mGestureState.getRunningTask();
- if (mIsSwipeForSplit) {
- int[] splitTaskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds();
- runningTasks = cachedTaskInfo.getSplitPlaceholderTasks(splitTaskIds);
- } else {
- runningTasks = cachedTaskInfo.getPlaceholderTasks();
- }
+ int[] splitTaskIds = mIsSwipeForSplit
+ ? TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds()
+ : null;
+ GroupedTaskInfo groupedTaskInfo =
+ mGestureState.getRunningTask().getPlaceholderGroupedTaskInfo(splitTaskIds);
// Safeguard against any null tasks being sent to recents view, happens when quickswitching
// very quickly w/ split tasks because TopTaskTracker provides stale information compared to
// actual running tasks in the recents animation.
// TODO(b/236226779), Proper fix (ag/22237143)
- if (Arrays.stream(runningTasks).anyMatch(Objects::isNull)) {
+ if (groupedTaskInfo == null) {
return;
}
if (mRecentsView == null) {
return;
}
- mRecentsView.onGestureAnimationStart(runningTasks);
+ mRecentsView.onGestureAnimationStart(groupedTaskInfo);
TaskView currentPageTaskView = mRecentsView.getCurrentPageTaskView();
if (currentPageTaskView != null) {
mPreviousTaskViewType = currentPageTaskView.getType();
@@ -1614,7 +1610,7 @@ public abstract class AbsSwipeUpHandler<
TaskStackChangeListeners.getInstance().registerTaskStackListener(
mActivityRestartListener);
- mParallelRunningAnim = mContainerInterface.getParallelAnimationToLauncher(
+ mParallelRunningAnim = mContainerInterface.getParallelAnimationToGestureEndTarget(
mGestureState.getEndTarget(), duration,
mTaskAnimationManager.getCurrentCallbacks());
if (mParallelRunningAnim != null) {
diff --git a/quickstep/src/com/android/quickstep/AspectRatioSystemShortcut.kt b/quickstep/src/com/android/quickstep/AspectRatioSystemShortcut.kt
new file mode 100644
index 0000000000..68860ac91b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/AspectRatioSystemShortcut.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import android.content.Intent
+import android.provider.Settings
+import android.view.View
+import androidx.core.net.toUri
+import com.android.launcher3.AbstractFloatingViewHelper
+import com.android.launcher3.R
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent
+import com.android.launcher3.popup.SystemShortcut
+import com.android.quickstep.views.RecentsViewContainer
+import com.android.quickstep.views.TaskContainer
+import com.android.window.flags.Flags.universalResizableByDefault
+
+/**
+ * System shortcut to change the application's aspect ratio compatibility mode.
+ *
+ * This shows up only on screens that are not compact, ie. shortest-width greater than {@link
+ * com.android.launcher3.util.window.WindowManagerProxy#MIN_TABLET_WIDTH}.
+ */
+class AspectRatioSystemShortcut(
+ viewContainer: RecentsViewContainer,
+ taskContainer: TaskContainer,
+ abstractFloatingViewHelper: AbstractFloatingViewHelper,
+) :
+ SystemShortcut<RecentsViewContainer>(
+ R.drawable.ic_aspect_ratio,
+ R.string.recent_task_option_aspect_ratio,
+ viewContainer,
+ taskContainer.itemInfo,
+ taskContainer.taskView,
+ abstractFloatingViewHelper,
+ ) {
+ override fun onClick(view: View) {
+ dismissTaskMenuView()
+
+ val intent =
+ Intent(Settings.ACTION_MANAGE_USER_ASPECT_RATIO_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ if (mItemInfo.targetPackage != null) {
+ intent.setData(("package:" + mItemInfo.targetPackage).toUri())
+ }
+
+ mTarget.startActivitySafely(view, intent, mItemInfo)
+ mTarget
+ .statsLogManager
+ .logger()
+ .withItemInfo(mItemInfo)
+ .log(LauncherEvent.LAUNCHER_ASPECT_RATIO_SETTINGS_SYSTEM_SHORTCUT_TAP)
+ }
+
+ companion object {
+ /** Optionally create a factory for the aspect ratio system shortcut. */
+ @JvmOverloads
+ fun createFactory(
+ abstractFloatingViewHelper: AbstractFloatingViewHelper = AbstractFloatingViewHelper()
+ ): TaskShortcutFactory {
+ return object : TaskShortcutFactory {
+ override fun getShortcuts(
+ viewContainer: RecentsViewContainer,
+ taskContainer: TaskContainer,
+ ): List<AspectRatioSystemShortcut>? {
+ return when {
+ // Only available when the feature flag is on.
+ !universalResizableByDefault() -> null
+
+ // The option is only shown on sw600dp+ screens (checked by isTablet)
+ !viewContainer.deviceProfile.isTablet -> null
+
+ else -> {
+ listOf(
+ AspectRatioSystemShortcut(
+ viewContainer,
+ taskContainer,
+ abstractFloatingViewHelper,
+ )
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/BaseContainerInterface.java b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
index c64067aa58..244dfc3971 100644
--- a/quickstep/src/com/android/quickstep/BaseContainerInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseContainerInterface.java
@@ -182,7 +182,7 @@ public abstract class BaseContainerInterface<STATE_TYPE extends BaseState<STATE_
* Called when the gesture ends and the animation starts towards the given target. Used to add
* an optional additional animation with the same duration.
*/
- public @Nullable Animator getParallelAnimationToLauncher(
+ public @Nullable Animator getParallelAnimationToGestureEndTarget(
GestureState.GestureEndTarget endTarget, long duration,
RecentsAnimationCallbacks callbacks) {
if (endTarget == RECENTS) {
diff --git a/quickstep/src/com/android/quickstep/DisplayModel.kt b/quickstep/src/com/android/quickstep/DisplayModel.kt
index ac943757fa..0b8af4045b 100644
--- a/quickstep/src/com/android/quickstep/DisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/DisplayModel.kt
@@ -22,12 +22,14 @@ import android.util.Log
import android.util.SparseArray
import android.view.Display
import androidx.core.util.valueIterator
-import com.android.launcher3.util.Executors
import com.android.quickstep.DisplayModel.DisplayResource
+import com.android.quickstep.SystemDecorationChangeObserver.Companion.INSTANCE
+import com.android.quickstep.SystemDecorationChangeObserver.DisplayDecorationListener
import java.io.PrintWriter
/** data model for managing resources with lifecycles that match that of the connected display */
-abstract class DisplayModel<RESOURCE_TYPE : DisplayResource>(val context: Context) {
+abstract class DisplayModel<RESOURCE_TYPE : DisplayResource>(val context: Context) :
+ DisplayDecorationListener {
companion object {
private const val TAG = "DisplayModel"
@@ -35,53 +37,54 @@ abstract class DisplayModel<RESOURCE_TYPE : DisplayResource>(val context: Contex
}
private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ private var systemDecorationChangeObserver: SystemDecorationChangeObserver? = null
protected val displayResourceArray = SparseArray<RESOURCE_TYPE>()
- private val displayListener: DisplayManager.DisplayListener =
- (object : DisplayManager.DisplayListener {
- override fun onDisplayAdded(displayId: Int) {
- if (DEBUG) Log.d(TAG, "onDisplayAdded: displayId=$displayId")
- storeDisplayResource(displayId)
- }
+ override fun onDisplayAddSystemDecorations(displayId: Int) {
+ if (DEBUG) Log.d(TAG, "onDisplayAdded: displayId=$displayId")
+ storeDisplayResource(displayId)
+ }
- override fun onDisplayRemoved(displayId: Int) {
- if (DEBUG) Log.d(TAG, "onDisplayRemoved: displayId=$displayId")
- deleteDisplayResource(displayId)
- }
+ override fun onDisplayRemoved(displayId: Int) {
+ if (DEBUG) Log.d(TAG, "onDisplayRemoved: displayId=$displayId")
+ deleteDisplayResource(displayId)
+ }
- override fun onDisplayChanged(displayId: Int) {
- if (DEBUG) Log.d(TAG, "onDisplayChanged: displayId=$displayId")
- }
- })
+ override fun onDisplayRemoveSystemDecorations(displayId: Int) {
+ if (DEBUG) Log.d(TAG, "onDisplayRemoveSystemDecorations: displayId=$displayId")
+ deleteDisplayResource(displayId)
+ }
protected abstract fun createDisplayResource(display: Display): RESOURCE_TYPE
- protected fun registerDisplayListener() {
- displayManager.registerDisplayListener(displayListener, Executors.MAIN_EXECUTOR.handler)
- // In the scenario where displays were added before this display listener was
- // registered, we should store the DisplayResources for those displays directly.
+ protected fun initializeDisplays() {
+ systemDecorationChangeObserver = INSTANCE[context]
+ systemDecorationChangeObserver?.registerDisplayDecorationListener(this)
displayManager.displays
.filter { getDisplayResource(it.displayId) == null }
.forEach { storeDisplayResource(it.displayId) }
}
fun destroy() {
+ systemDecorationChangeObserver?.unregisterDisplayDecorationListener(this)
+ systemDecorationChangeObserver = null
displayResourceArray.valueIterator().forEach { displayResource ->
displayResource.cleanup()
}
displayResourceArray.clear()
- displayManager.unregisterDisplayListener(displayListener)
}
fun getDisplayResource(displayId: Int): RESOURCE_TYPE? {
- if (DEBUG) Log.d(TAG, "get: displayId=$displayId")
+ if (DEBUG) Log.d(TAG, Log.getStackTraceString(Throwable("get: displayId=$displayId")))
return displayResourceArray[displayId]
}
fun deleteDisplayResource(displayId: Int) {
if (DEBUG) Log.d(TAG, "delete: displayId=$displayId")
- getDisplayResource(displayId)?.cleanup()
- displayResourceArray.remove(displayId)
+ getDisplayResource(displayId)?.let {
+ it.cleanup()
+ displayResourceArray.remove(displayId)
+ }
}
fun storeDisplayResource(displayId: Int) {
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index d8e0296a68..7654471555 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -203,16 +203,16 @@ public final class FallbackActivityInterface extends
}
@Override
- public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
+ public @Nullable Animator getParallelAnimationToGestureEndTarget(GestureEndTarget endTarget,
long duration, RecentsAnimationCallbacks callbacks) {
FallbackTaskbarUIController uiController = getTaskbarController();
- Animator superAnimator = super.getParallelAnimationToLauncher(
+ Animator superAnimator = super.getParallelAnimationToGestureEndTarget(
endTarget, duration, callbacks);
if (uiController == null) {
return superAnimator;
}
- RecentsState toState = stateFromGestureEndTarget(endTarget);
- Animator taskbarAnimator = uiController.createAnimToRecentsState(toState, duration);
+ Animator taskbarAnimator = uiController.getParallelAnimationToGestureEndTarget(
+ endTarget, duration, callbacks);
if (taskbarAnimator == null) {
return superAnimator;
}
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 7d8a53d61b..9365383880 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -218,7 +218,8 @@ public class FallbackSwipeHandler extends
if (mRunningOverHome) {
if (DisplayController.getNavigationMode(mContext).hasGestures) {
mRecentsView.onGestureAnimationStartOnHome(
- mGestureState.getRunningTask().getPlaceholderTasks());
+ mGestureState.getRunningTask().getPlaceholderGroupedTaskInfo(
+ /* splitTaskIds = */ null));
}
} else {
super.notifyGestureAnimationStartToRecents();
diff --git a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
index 35630ef57f..503f047e4f 100644
--- a/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackWindowInterface.java
@@ -31,7 +31,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.taskbar.FallbackTaskbarUIController;
+import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.fallback.RecentsState;
@@ -104,14 +104,9 @@ public final class FallbackWindowInterface extends BaseWindowInterface{
}
@Override
- public FallbackTaskbarUIController getTaskbarController() {
+ public TaskbarUIController getTaskbarController() {
RecentsWindowManager manager = getCreatedContainer();
- if (manager == null) {
- return null;
- }
- return null;
- // todo b/365775636: pass a taskbar implementation
- // return manager.getTaskbarUIController();
+ return manager == null ? null : manager.getTaskbarUIController();
}
@Override
@@ -213,16 +208,16 @@ public final class FallbackWindowInterface extends BaseWindowInterface{
}
@Override
- public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
+ public @Nullable Animator getParallelAnimationToGestureEndTarget(GestureEndTarget endTarget,
long duration, RecentsAnimationCallbacks callbacks) {
- FallbackTaskbarUIController uiController = getTaskbarController();
- Animator superAnimator = super.getParallelAnimationToLauncher(
+ TaskbarUIController uiController = getTaskbarController();
+ Animator superAnimator = super.getParallelAnimationToGestureEndTarget(
endTarget, duration, callbacks);
if (uiController == null) {
return superAnimator;
}
- RecentsState toState = stateFromGestureEndTarget(endTarget);
- Animator taskbarAnimator = uiController.createAnimToRecentsState(toState, duration);
+ Animator taskbarAnimator = uiController.getParallelAnimationToGestureEndTarget(
+ endTarget, duration, callbacks);
if (taskbarAnimator == null) {
return superAnimator;
}
diff --git a/quickstep/src/com/android/quickstep/HomeVisibilityState.kt b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
index 1345e0b344..020b9e2fab 100644
--- a/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
+++ b/quickstep/src/com/android/quickstep/HomeVisibilityState.kt
@@ -18,6 +18,9 @@ package com.android.quickstep
import android.os.RemoteException
import android.util.Log
+import android.view.InsetsState
+import android.view.WindowInsets
+
import com.android.launcher3.Utilities
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.util.Executors
@@ -30,6 +33,8 @@ class HomeVisibilityState {
var isHomeVisible = true
private set
+ @Volatile var navbarInsetPosition = 0
+
private var listeners = mutableSetOf<VisibilityChangeListener>()
fun addListener(l: VisibilityChangeListener) = listeners.add(l)
@@ -50,6 +55,11 @@ class HomeVisibilityState {
},
)
}
+ override fun onDisplayInsetsChanged(insetsState: InsetsState) {
+ val bottomInset = insetsState.calculateInsets(insetsState.displayFrame,
+ WindowInsets.Type.navigationBars(), false).bottom
+ navbarInsetPosition = insetsState.displayFrame.bottom - bottomInset
+ }
}
)
} catch (e: RemoteException) {
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 081ed9d517..e5cbc66425 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -63,14 +63,26 @@ public interface InputConsumer {
"TYPE_BUBBLE_BAR", // 16
};
- InputConsumer NO_OP = () -> TYPE_NO_OP;
+ InputConsumer DEFAULT_NO_OP = createNoOpInputConsumer(Display.DEFAULT_DISPLAY);
- int getType();
+ static InputConsumer createNoOpInputConsumer(int displayId) {
+ return new InputConsumer() {
+ @Override
+ public int getType() {
+ return TYPE_NO_OP;
+ }
- default int getDisplayId() {
- return Display.DEFAULT_DISPLAY;
+ @Override
+ public int getDisplayId() {
+ return displayId;
+ }
+ };
}
+ int getType();
+
+ int getDisplayId();
+
/**
* Returns true if the user has crossed the threshold for it to be an explicit action.
*/
diff --git a/quickstep/src/com/android/quickstep/InputConsumerUtils.kt b/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
index cd3ac12c82..89b5b291c6 100644
--- a/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
+++ b/quickstep/src/com/android/quickstep/InputConsumerUtils.kt
@@ -57,7 +57,7 @@ object InputConsumerUtils {
@JvmStatic
fun <S : BaseState<S>, T> newConsumer(
context: Context,
- resetGestureInputConsumer: ResetGestureInputConsumer?,
+ userUnlocked: Boolean,
overviewComponentObserver: OverviewComponentObserver,
deviceState: RecentsAnimationDeviceState,
previousGestureState: GestureState,
@@ -122,7 +122,8 @@ object InputConsumerUtils {
// camera).
createDeviceLockedInputConsumer(
context,
- resetGestureInputConsumer,
+ userUnlocked,
+ taskbarManager,
deviceState,
gestureState,
taskAnimationManager,
@@ -131,7 +132,10 @@ object InputConsumerUtils {
)
} else {
getDefaultInputConsumer(
- resetGestureInputConsumer,
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
reasonString.append("%scannot start system gesture", SUBSTRING_PREFIX),
)
}
@@ -153,7 +157,8 @@ object InputConsumerUtils {
base =
newBaseConsumer<S, T>(
context,
- resetGestureInputConsumer,
+ userUnlocked,
+ taskbarManager,
overviewComponentObserver,
deviceState,
previousGestureState,
@@ -172,7 +177,14 @@ object InputConsumerUtils {
"cannot start system gesture and recents " +
"animation was not running, trying to use default input consumer"
)
- base = getDefaultInputConsumer(resetGestureInputConsumer, reasonString)
+ base =
+ getDefaultInputConsumer(
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
+ reasonString,
+ )
}
if (deviceState.isGesturalNavMode || gestureState.isTrackpadGesture) {
handleOrientationSetup(base)
@@ -237,7 +249,14 @@ object InputConsumerUtils {
SUBSTRING_PREFIX,
)
// Bubbles can handle home gesture itself.
- base = getDefaultInputConsumer(resetGestureInputConsumer, reasonString)
+ base =
+ getDefaultInputConsumer(
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
+ reasonString,
+ )
}
}
@@ -279,7 +298,14 @@ object InputConsumerUtils {
SUBSTRING_PREFIX,
)
// Bubbles can handle home gesture itself.
- base = getDefaultInputConsumer(resetGestureInputConsumer, reasonString)
+ base =
+ getDefaultInputConsumer(
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
+ reasonString,
+ )
}
}
@@ -374,7 +400,14 @@ object InputConsumerUtils {
"%sscreen pinning is active, trying to use default input consumer",
SUBSTRING_PREFIX,
)
- base = getDefaultInputConsumer(resetGestureInputConsumer, reasonString)
+ base =
+ getDefaultInputConsumer(
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
+ reasonString,
+ )
}
if (deviceState.canTriggerOneHandedAction(event)) {
@@ -411,7 +444,7 @@ object InputConsumerUtils {
context,
deviceState,
inputMonitorCompat,
- InputConsumer.NO_OP,
+ InputConsumer.createNoOpInputConsumer(gestureState.displayId),
gestureState,
motionEvent,
CompoundString.NO_OP,
@@ -450,7 +483,8 @@ object InputConsumerUtils {
@JvmStatic
fun <S : BaseState<S>, T> newBaseConsumer(
context: Context,
- resetGestureInputConsumer: ResetGestureInputConsumer?,
+ userUnlocked: Boolean,
+ taskbarManager: TaskbarManager,
overviewComponentObserver: OverviewComponentObserver,
deviceState: RecentsAnimationDeviceState,
previousGestureState: GestureState,
@@ -467,7 +501,8 @@ object InputConsumerUtils {
// This handles apps showing over the lockscreen (e.g. camera)
return createDeviceLockedInputConsumer(
context,
- resetGestureInputConsumer,
+ userUnlocked,
+ taskbarManager,
deviceState,
gestureState,
taskAnimationManager,
@@ -515,13 +550,15 @@ object InputConsumerUtils {
(com.android.launcher3.Flags.useActivityOverlay() &&
runningTask != null &&
runningTask.isHomeTask &&
- overviewComponentObserver.isHomeAndOverviewSame &&
+ overviewComponentObserver.isHomeAndOverviewSameActivity &&
!launcherResumedThroughShellTransition &&
!previousGestureState.isRecentsAnimationRunning)
return if (gestureState.getContainerInterface<S, T>().isInLiveTileMode()) {
createOverviewInputConsumer<S, T>(
- resetGestureInputConsumer,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
deviceState,
inputMonitorCompat,
previousGestureState,
@@ -534,7 +571,10 @@ object InputConsumerUtils {
)
} else if (runningTask == null) {
getDefaultInputConsumer(
- resetGestureInputConsumer,
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
reasonString.append("%srunning task == null", SUBSTRING_PREFIX),
)
} else if (
@@ -543,7 +583,9 @@ object InputConsumerUtils {
forceOverviewInputConsumer
) {
createOverviewInputConsumer<S, T>(
- resetGestureInputConsumer,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
deviceState,
inputMonitorCompat,
previousGestureState,
@@ -565,7 +607,10 @@ object InputConsumerUtils {
)
} else if (deviceState.isGestureBlockedTask(runningTask) || launcherChildActivityResumed) {
getDefaultInputConsumer(
- resetGestureInputConsumer,
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
reasonString.append(
if (launcherChildActivityResumed)
"%sis launcher child-task, trying to use default input consumer"
@@ -592,7 +637,8 @@ object InputConsumerUtils {
private fun createDeviceLockedInputConsumer(
context: Context,
- resetGestureInputConsumer: ResetGestureInputConsumer?,
+ userUnlocked: Boolean,
+ taskbarManager: TaskbarManager,
deviceState: RecentsAnimationDeviceState,
gestureState: GestureState,
taskAnimationManager: TaskAnimationManager,
@@ -617,7 +663,10 @@ object InputConsumerUtils {
)
} else {
getDefaultInputConsumer(
- resetGestureInputConsumer,
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
reasonString.append(
if (deviceState.isFullyGesturalNavMode || gestureState.isTrackpadGesture)
"%srunning task == null, trying to use default input consumer"
@@ -631,7 +680,9 @@ object InputConsumerUtils {
}
private fun <S : BaseState<S>, T> createOverviewInputConsumer(
- resetGestureInputConsumer: ResetGestureInputConsumer?,
+ userUnlocked: Boolean,
+ taskAnimationManager: TaskAnimationManager,
+ taskbarManager: TaskbarManager,
deviceState: RecentsAnimationDeviceState,
inputMonitorCompat: InputMonitorCompat,
previousGestureState: GestureState,
@@ -642,7 +693,10 @@ object InputConsumerUtils {
val container: T =
gestureState.getContainerInterface<S, T>().getCreatedContainer()
?: return getDefaultInputConsumer(
- resetGestureInputConsumer,
+ gestureState.displayId,
+ userUnlocked,
+ taskAnimationManager,
+ taskbarManager,
reasonString.append(
"%sactivity == null, trying to use default input consumer",
SUBSTRING_PREFIX,
@@ -694,24 +748,34 @@ object InputConsumerUtils {
}
/** Returns the [ResetGestureInputConsumer] if user is unlocked, else NO_OP. */
- private fun getDefaultInputConsumer(
- resetGestureInputConsumer: ResetGestureInputConsumer?,
+ @JvmStatic
+ fun getDefaultInputConsumer(
+ displayId: Int,
+ userUnlocked: Boolean,
+ taskAnimationManager: TaskAnimationManager?,
+ taskbarManager: TaskbarManager?,
reasonString: CompoundString,
): InputConsumer {
- return if (resetGestureInputConsumer != null) {
+ return if (userUnlocked && taskAnimationManager != null && taskbarManager != null) {
reasonString.append(
- "%smResetGestureInputConsumer initialized, using ResetGestureInputConsumer",
+ "%sResetGestureInputConsumer available, using ResetGestureInputConsumer",
SUBSTRING_PREFIX,
)
- resetGestureInputConsumer
+ ResetGestureInputConsumer(displayId, taskAnimationManager) {
+ taskbarManager.getTaskbarForDisplay(displayId)
+ }
} else {
reasonString.append(
- "%smResetGestureInputConsumer not initialized, using no-op input consumer",
+ "%s${
+ if (userUnlocked) "user is locked"
+ else if (taskAnimationManager == null) "taskAnimationManager is null"
+ else "taskbarManager is null"
+ }, using no-op input consumer",
SUBSTRING_PREFIX,
)
- // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
+ // ResetGestureInputConsumer isn't available until onUserUnlocked(), so reset to
// NO_OP until then (we never want these to be null).
- InputConsumer.NO_OP
+ InputConsumer.createNoOpInputConsumer(displayId)
}
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index ac0aa76ad5..fa8e4846b3 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -261,7 +261,8 @@ public final class LauncherActivityInterface extends
return launcher != null
&& launcher.getStateManager().getState() == OVERVIEW
&& launcher.isStarted()
- && TopTaskTracker.INSTANCE.get(launcher).getCachedTopTask(false).isHomeTask();
+ && TopTaskTracker.INSTANCE.get(launcher).getCachedTopTask(false,
+ launcher.getDisplayId()).isHomeTask();
}
private boolean isInMinusOne() {
@@ -270,7 +271,8 @@ public final class LauncherActivityInterface extends
return launcher != null
&& launcher.getStateManager().getState() == NORMAL
&& !launcher.isStarted()
- && TopTaskTracker.INSTANCE.get(launcher).getCachedTopTask(false).isHomeTask();
+ && TopTaskTracker.INSTANCE.get(launcher).getCachedTopTask(false,
+ launcher.getDisplayId()).isHomeTask();
}
@Override
@@ -298,16 +300,16 @@ public final class LauncherActivityInterface extends
}
@Override
- public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
+ public @Nullable Animator getParallelAnimationToGestureEndTarget(GestureEndTarget endTarget,
long duration, RecentsAnimationCallbacks callbacks) {
LauncherTaskbarUIController uiController = getTaskbarController();
- Animator superAnimator = super.getParallelAnimationToLauncher(
+ Animator superAnimator = super.getParallelAnimationToGestureEndTarget(
endTarget, duration, callbacks);
if (uiController == null || callbacks == null) {
return superAnimator;
}
- LauncherState toState = stateFromGestureEndTarget(endTarget);
- Animator taskbarAnimator = uiController.createAnimToLauncher(toState, callbacks, duration);
+ Animator taskbarAnimator = uiController.getParallelAnimationToGestureEndTarget(endTarget,
+ duration, callbacks);
if (superAnimator == null) {
return taskbarAnimator;
} else {
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 2ff42cd991..c6785629b0 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -26,6 +26,7 @@ import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
import static com.android.window.flags.Flags.predictiveBackThreeButtonNav;
+import static com.android.window.flags.Flags.removeDepartTargetFromMotion;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -121,6 +122,7 @@ public class LauncherBackAnimationController {
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private float mBackProgress = 0;
private boolean mBackInProgress = false;
+ private boolean mWaitStartTransition = false;
private OnBackInvokedCallbackStub mBackCallback;
private IRemoteAnimationFinishedCallback mAnimationFinishedCallback;
private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
@@ -158,7 +160,8 @@ public class LauncherBackAnimationController {
mBackCallback = new OnBackInvokedCallbackStub(handler, mProgressAnimator,
mProgressInterpolator, this);
SystemUiProxy.INSTANCE.get(mLauncher).setBackToLauncherCallback(mBackCallback,
- new RemoteAnimationRunnerStub(this));
+ new RemoteAnimationRunnerStub(this,
+ removeDepartTargetFromMotion() ? handler : null));
}
private static class OnBackInvokedCallbackStub extends IOnBackInvokedCallback.Stub {
@@ -195,7 +198,14 @@ public class LauncherBackAnimationController {
mHandler.post(() -> {
LauncherBackAnimationController controller = mControllerRef.get();
if (controller != null) {
- controller.startTransition();
+ if (!removeDepartTargetFromMotion()) {
+ controller.startTransition();
+ } else {
+ controller.mWaitStartTransition = true;
+ if (controller.mBackTarget != null && controller.mBackInProgress) {
+ controller.startTransition();
+ }
+ }
}
mProgressAnimator.reset();
});
@@ -221,6 +231,7 @@ public class LauncherBackAnimationController {
LauncherBackAnimationController controller = mControllerRef.get();
if (controller != null) {
controller.initBackMotion(backEvent);
+ controller.tryStartBackAnimation();
mProgressAnimator.onBackStarted(backEvent, event -> {
float backProgress = event.getProgress();
controller.mBackProgress =
@@ -248,9 +259,12 @@ public class LauncherBackAnimationController {
// LauncherBackAnimationController has strong reference to Launcher activity, the binder
// callback should not hold strong reference to it to avoid memory leak.
private WeakReference<LauncherBackAnimationController> mControllerRef;
+ private final Handler mHandler;
- private RemoteAnimationRunnerStub(LauncherBackAnimationController controller) {
+ private RemoteAnimationRunnerStub(LauncherBackAnimationController controller,
+ Handler handler) {
mControllerRef = new WeakReference<>(controller);
+ mHandler = handler;
}
@Override
@@ -261,16 +275,29 @@ public class LauncherBackAnimationController {
if (controller == null) {
return;
}
- for (final RemoteAnimationTarget target : apps) {
- if (MODE_CLOSING == target.mode) {
- controller.mBackTarget = target;
+ final Runnable r = () -> {
+ for (final RemoteAnimationTarget target : apps) {
+ if (MODE_CLOSING == target.mode) {
+ controller.mBackTarget = target;
+ }
+ if (MODE_OPENING == target.mode) {
+ controller.mLauncherTarget = target;
+ }
+ }
+ controller.mAnimationFinishedCallback = finishedCallback;
+ if (!removeDepartTargetFromMotion()) {
+ return;
}
- if (MODE_OPENING == target.mode) {
- controller.mLauncherTarget = target;
+ controller.tryStartBackAnimation();
+ if (controller.mWaitStartTransition) {
+ controller.startTransition();
}
+ };
+ if (mHandler != null) {
+ mHandler.post(r);
+ } else {
+ r.run();
}
- controller.mAnimationFinishedCallback = finishedCallback;
- controller.startBack();
}
@Override
@@ -304,11 +331,18 @@ public class LauncherBackAnimationController {
// we don't have to handle that case.
mProgressAnimator.removeOnBackCancelledFinishCallback();
+ if (!removeDepartTargetFromMotion()) {
+ RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
+ if (appTarget == null || appTarget.leash == null || !appTarget.leash.isValid()) {
+ return;
+ }
+ mBackTarget = appTarget;
+ }
mBackInProgress = true;
mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
}
- private void startBack() {
- if (mBackTarget == null) {
+ private void tryStartBackAnimation() {
+ if (mBackTarget == null || (removeDepartTargetFromMotion() && !mBackInProgress)) {
return;
}
@@ -465,10 +499,14 @@ public class LauncherBackAnimationController {
}
private void startTransition() {
- if (mBackTarget == null) {
- // Trigger transition system instead of custom transition animation.
- finishAnimation();
- return;
+ if (!removeDepartTargetFromMotion()) {
+ if (mBackTarget == null) {
+ // Trigger transition system instead of custom transition animation.
+ finishAnimation();
+ return;
+ }
+ } else {
+ mWaitStartTransition = false;
}
if (mLauncher.isDestroyed()) {
return;
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index 20dfb1039b..8b9a3daea8 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -75,7 +75,6 @@ class OverviewCommandHelper
constructor(
private val touchInteractionService: TouchInteractionService,
private val overviewComponentObserver: OverviewComponentObserver,
- private val taskAnimationManager: TaskAnimationManager,
private val dispatcherProvider: DispatcherProvider = ProductionDispatchers,
private val recentsDisplayModel: RecentsDisplayModel,
private val focusState: FocusState,
@@ -92,27 +91,33 @@ constructor(
*/
private var keyboardTaskFocusIndex = -1
- // TODO (b/397942185): get per-display interface
- private val containerInterface: BaseContainerInterface<*, *>
- get() = overviewComponentObserver.getContainerInterface(DEFAULT_DISPLAY)
+ private fun getContainerInterface(displayId: Int) =
+ overviewComponentObserver.getContainerInterface(displayId)
- // TODO (b/397942185): get per-display RecentsView
- private val visibleRecentsView: RecentsView<*, *>?
- get() = containerInterface.getVisibleRecentsView<RecentsView<*, *>>()
+ private fun getVisibleRecentsView(displayId: Int) =
+ getContainerInterface(displayId).getVisibleRecentsView<RecentsView<*, *>>()
/**
* Adds a command to be executed next, after all pending tasks are completed. Max commands that
* can be queued is [.MAX_QUEUE_SIZE]. Requests after reaching that limit will be silently
* dropped.
+ *
+ * @param type The type of the command
+ * @param onDisplays The display to run the command on
*/
@BinderThread
- fun addCommand(type: CommandType): CommandInfo? {
+ @JvmOverloads
+ fun addCommand(
+ type: CommandType,
+ displayId: Int = DEFAULT_DISPLAY,
+ isLastOfBatch: Boolean = true,
+ ): CommandInfo? {
if (commandQueue.size >= MAX_QUEUE_SIZE) {
Log.d(TAG, "command not added: $type - queue is full ($commandQueue).")
return null
}
- val command = CommandInfo(type)
+ val command = CommandInfo(type, displayId = displayId, isLastOfBatch = isLastOfBatch)
commandQueue.add(command)
Log.d(TAG, "command added: $command")
@@ -130,6 +135,35 @@ constructor(
return command
}
+ @BinderThread
+ fun addCommandsForDisplays(type: CommandType, displayIds: IntArray): CommandInfo? {
+ if (displayIds.isEmpty()) return null
+ var lastCommand: CommandInfo? = null
+ displayIds.forEachIndexed({ i, displayId ->
+ lastCommand = addCommand(type, displayId, i == displayIds.size - 1)
+ })
+ return lastCommand
+ }
+
+ @BinderThread
+ fun addCommandsForAllDisplays(type: CommandType) =
+ addCommandsForDisplays(
+ type,
+ recentsDisplayModel.activeDisplayResources
+ .map { resource -> resource.displayId }
+ .toIntArray(),
+ )
+
+ @BinderThread
+ fun addCommandsForDisplaysExcept(type: CommandType, excludedDisplayId: Int) =
+ addCommandsForDisplays(
+ type,
+ recentsDisplayModel.activeDisplayResources
+ .map { resource -> resource.displayId }
+ .filter { displayId -> displayId != excludedDisplayId }
+ .toIntArray(),
+ )
+
fun canStartHomeSafely(): Boolean = commandQueue.isEmpty() || commandQueue.first().type == HOME
/** Clear pending or completed commands from the queue */
@@ -144,7 +178,7 @@ constructor(
* completion (returns false).
*/
@UiThread
- private fun processNextCommand() =
+ private fun processNextCommand(): Unit =
traceSection("OverviewCommandHelper.processNextCommand") {
val command: CommandInfo? = commandQueue.firstOrNull()
if (command == null) {
@@ -183,7 +217,7 @@ constructor(
*/
@VisibleForTesting
fun executeCommand(command: CommandInfo, onCallbackResult: () -> Unit): Boolean {
- val recentsView = visibleRecentsView
+ val recentsView = getVisibleRecentsView(command.displayId)
Log.d(TAG, "executeCommand: $command - visibleRecentsView: $recentsView")
return if (recentsView != null) {
executeWhenRecentsIsVisible(command, recentsView, onCallbackResult)
@@ -231,6 +265,7 @@ constructor(
launchTask(recentsView, taskView, command, onCallbackResult)
}
}
+
TOGGLE -> {
launchTask(
recentsView,
@@ -239,6 +274,7 @@ constructor(
onCallbackResult,
)
}
+
HOME -> {
recentsView.startHome()
true
@@ -295,6 +331,7 @@ constructor(
command: CommandInfo,
onCallbackResult: () -> Unit,
): Boolean {
+ val containerInterface = getContainerInterface(command.displayId)
val recentsViewContainer = containerInterface.getCreatedContainer()
val recentsView: RecentsView<*, *>? = recentsViewContainer?.getOverviewPanel()
val deviceProfile = recentsViewContainer?.getDeviceProfile()
@@ -336,6 +373,7 @@ constructor(
if (keyboardTaskFocusIndex == -1) return true
}
+
KEYBOARD_INPUT ->
if (uiController != null && deviceProfile?.isTablet == true) {
if (
@@ -349,6 +387,7 @@ constructor(
} else {
keyboardTaskFocusIndex = 0
}
+
HOME -> {
ActiveGestureProtoLogProxy.logExecuteHomeCommand()
// Although IActivityTaskManager$Stub$Proxy.startActivity is a slow binder call,
@@ -358,12 +397,14 @@ constructor(
touchInteractionService.startActivity(overviewComponentObserver.homeIntent)
return true
}
+
SHOW ->
// When Recents is not currently visible, the command's type is SHOW
// when overview is triggered via the keyboard overview button or Action+Tab
// keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button
// nav is TYPE_TOGGLE.
keyboardTaskFocusIndex = 0
+
TOGGLE -> {}
}
@@ -379,7 +420,7 @@ constructor(
Log.d(TAG, "switching to Overview state - onAnimationStart: $command")
super.onAnimationStart(animation)
updateRecentsViewFocus(command)
- logShowOverviewFrom(command.type)
+ logShowOverviewFrom(command)
}
override fun onAnimationEnd(animation: Animator) {
@@ -403,16 +444,16 @@ constructor(
val gestureState =
touchInteractionService.createGestureState(
- focusedDisplayId,
+ command.displayId,
GestureState.DEFAULT_STATE,
GestureState.TrackpadGestureType.NONE,
)
gestureState.isHandlingAtomicEvent = true
val interactionHandler =
- touchInteractionService.swipeUpHandlerFactory.newHandler(
- gestureState,
- command.createTime,
- )
+ touchInteractionService
+ // TODO(b/404757863): use command.displayId instead of focusedDisplayId.
+ .getSwipeUpHandlerFactory(focusedDisplayId)
+ .newHandler(gestureState, command.createTime)
interactionHandler.setGestureEndCallback {
onTransitionComplete(command, interactionHandler, onCallbackResult)
}
@@ -433,7 +474,7 @@ constructor(
}
updateRecentsViewFocus(command)
- logShowOverviewFrom(command.type)
+ logShowOverviewFrom(command)
containerInterface.runOnInitBackgroundStateUI {
Log.d(TAG, "recents animation started - onInitBackgroundStateUI: $command")
interactionHandler.onGestureEnded(
@@ -457,6 +498,15 @@ constructor(
}
}
+ val taskAnimationManager =
+ recentsDisplayModel.getTaskAnimationManager(command.displayId)
+ ?: run {
+ Log.e(TAG, "No TaskAnimationManager found for display ${command.displayId}")
+ ActiveGestureProtoLogProxy.logOnTaskAnimationManagerNotAvailable(
+ command.displayId
+ )
+ return false
+ }
if (taskAnimationManager.isRecentsAnimationRunning) {
command.setAnimationCallbacks(
taskAnimationManager.continueRecentsAnimation(gestureState)
@@ -519,7 +569,7 @@ constructor(
}
private fun updateRecentsViewFocus(command: CommandInfo) {
- val recentsView: RecentsView<*, *> = visibleRecentsView ?: return
+ val recentsView: RecentsView<*, *> = getVisibleRecentsView(command.displayId) ?: return
if (command.type != KEYBOARD_INPUT && command.type != HIDE && command.type != SHOW) {
return
}
@@ -540,7 +590,7 @@ constructor(
}
private fun onRecentsViewFocusUpdated(command: CommandInfo) {
- val recentsView: RecentsView<*, *> = visibleRecentsView ?: return
+ val recentsView: RecentsView<*, *> = getVisibleRecentsView(command.displayId) ?: return
if (command.type != HIDE || keyboardTaskFocusIndex == PagedView.INVALID_PAGE) {
return
}
@@ -558,10 +608,11 @@ constructor(
return true
}
- private fun logShowOverviewFrom(commandType: CommandType) {
+ private fun logShowOverviewFrom(command: CommandInfo) {
+ val containerInterface = getContainerInterface(command.displayId)
val container = containerInterface.getCreatedContainer() ?: return
val event =
- when (commandType) {
+ when (command.type) {
SHOW -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT
HIDE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH
TOGGLE -> LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON
@@ -594,6 +645,8 @@ constructor(
var status: CommandStatus = CommandStatus.IDLE,
val createTime: Long = SystemClock.elapsedRealtime(),
private var animationCallbacks: RecentsAnimationCallbacks? = null,
+ val displayId: Int = DEFAULT_DISPLAY,
+ val isLastOfBatch: Boolean = true,
) {
fun setAnimationCallbacks(recentsAnimationCallbacks: RecentsAnimationCallbacks) {
this.animationCallbacks = recentsAnimationCallbacks
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 7d3a1da76f..e7386dcb93 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -24,6 +24,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableFallbackOverviewInWindow;
import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableLauncherOverviewInWindow;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
@@ -50,7 +51,6 @@ import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
-import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.system.PackageManagerWrapper;
@@ -181,7 +181,7 @@ public final class OverviewComponentObserver {
mDefaultDisplayContainerInterface.onAssistantVisibilityChanged(0.f);
}
- if (SEPARATE_RECENTS_ACTIVITY.get() || enableLauncherOverviewInWindow.isTrue()) {
+ if (SEPARATE_RECENTS_ACTIVITY.get()) {
mIsDefaultHome = false;
if (defaultHome == null) {
defaultHome = mMyHomeIntent.getComponent();
@@ -194,8 +194,13 @@ public final class OverviewComponentObserver {
+ ", mIsDefaultHome=" + mIsDefaultHome);
if (!mIsHomeDisabled && (defaultHome == null || mIsDefaultHome)) {
- // User default home is same as out home app. Use Overview integrated in Launcher.
- mDefaultDisplayContainerInterface = LauncherActivityInterface.INSTANCE;
+ // User default home is same as our home app. Use Overview integrated in Launcher.
+ if (enableLauncherOverviewInWindow.isTrue()) {
+ mDefaultDisplayContainerInterface =
+ mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
+ } else {
+ mDefaultDisplayContainerInterface = LauncherActivityInterface.INSTANCE;
+ }
mIsHomeAndOverviewSame = true;
mOverviewIntent = mMyHomeIntent;
mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
@@ -204,7 +209,7 @@ public final class OverviewComponentObserver {
unregisterOtherHomeAppUpdateReceiver();
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
+ if (enableFallbackOverviewInWindow.isTrue()) {
mDefaultDisplayContainerInterface =
mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
} else {
@@ -290,12 +295,16 @@ public final class OverviewComponentObserver {
}
/**
- * Returns true if home and overview are same activity.
+ * Returns true if home and overview are same process.
*/
public boolean isHomeAndOverviewSame() {
return mIsHomeAndOverviewSame;
}
+ public boolean isHomeAndOverviewSameActivity() {
+ return isHomeAndOverviewSame() && !enableLauncherOverviewInWindow.isTrue();
+ }
+
/**
* Get the current control helper for managing interactions to the overview container for
* the given displayId.
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index cc5b2dabb7..a77241549c 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -504,20 +504,21 @@ public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityList
for (TaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
Task task = createTask(taskInfo, minimizedTaskIds);
List<Task> tasks = perDisplayTasks.computeIfAbsent(
- ExternalDisplaysKt.getDisplayId(task),
+ ExternalDisplaysKt.getSafeDisplayId(task),
k -> new ArrayList<>());
tasks.add(task);
}
// When the multiple desktop feature is disabled, there can only be up to a single desk
// on each display, The desk ID doesn't matter and should not be used.
return MapsKt.map(perDisplayTasks,
- it -> new DesktopTask(DesktopVisibilityController.INACTIVE_DESK_ID,
+ it -> new DesktopTask(DesktopVisibilityController.INACTIVE_DESK_ID, it.getKey(),
it.getValue()));
} else {
final int deskId = recentTaskInfo.getDeskId();
+ final int displayId = recentTaskInfo.getDeskDisplayId();
List<Task> tasks = CollectionsKt.map(recentTaskInfo.getTaskInfoList(),
it -> createTask(it, minimizedTaskIds));
- return List.of(new DesktopTask(deskId, tasks));
+ return List.of(new DesktopTask(deskId, displayId, tasks));
}
}
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index f96bbcb097..943f543809 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -68,8 +68,10 @@ public class RemoteTargetGluer {
* running tasks
*/
public RemoteTargetGluer(Context context, BaseContainerInterface sizingStrategy) {
+ // TODO: b/403344864 Make sure init with correct number of RemoteTargetHandle with
+ // multi-desks feature enabled as well.
int visibleTasksCount = DesktopVisibilityController.INSTANCE.get(context)
- .getVisibleDesktopTasksCount();
+ .getVisibleDesktopTasksCountDeprecated();
if (visibleTasksCount > 0) {
// Allocate +1 to account for a new task added to the desktop mode
int numHandles = visibleTasksCount + 1;
diff --git a/quickstep/src/com/android/quickstep/SystemDecorationChangeObserver.kt b/quickstep/src/com/android/quickstep/SystemDecorationChangeObserver.kt
new file mode 100644
index 0000000000..45594786aa
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SystemDecorationChangeObserver.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import android.content.Context
+import android.util.Log
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.util.DaggerSingletonObject
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.quickstep.dagger.QuickstepBaseAppComponent
+import javax.inject.Inject
+
+@LauncherAppSingleton
+class SystemDecorationChangeObserver @Inject constructor(@ApplicationContext context: Context) {
+ companion object {
+ private const val TAG = "SystemDecorationChangeObserver"
+ private const val DEBUG = false
+
+ @JvmStatic
+ val INSTANCE: DaggerSingletonObject<SystemDecorationChangeObserver> =
+ DaggerSingletonObject<SystemDecorationChangeObserver>(
+ QuickstepBaseAppComponent::getSystemDecorationChangeObserver
+ )
+ }
+
+ interface DisplayDecorationListener {
+ fun onDisplayAddSystemDecorations(displayId: Int)
+
+ fun onDisplayRemoved(displayId: Int)
+
+ fun onDisplayRemoveSystemDecorations(displayId: Int)
+ }
+
+ fun notifyAddSystemDecorations(displayId: Int) {
+ if (DEBUG) Log.d(TAG, "SystemDecorationAdded: $displayId")
+ for (listener in mDisplayDecorationListeners) {
+ MAIN_EXECUTOR.execute { listener.onDisplayAddSystemDecorations(displayId) }
+ }
+ }
+
+ fun notifyOnDisplayRemoved(displayId: Int) {
+ if (DEBUG) Log.d(TAG, "displayRemoved: $displayId")
+ for (listener in mDisplayDecorationListeners) {
+ MAIN_EXECUTOR.execute { listener.onDisplayRemoved(displayId) }
+ }
+ }
+
+ fun notifyDisplayRemoveSystemDecorations(displayId: Int) {
+ if (DEBUG) Log.d(TAG, "SystemDecorationRemoved: $displayId")
+ for (listener in mDisplayDecorationListeners) {
+ MAIN_EXECUTOR.execute { listener.onDisplayRemoveSystemDecorations(displayId) }
+ }
+ }
+
+ private val mDisplayDecorationListeners = ArrayList<DisplayDecorationListener>()
+
+ fun registerDisplayDecorationListener(listener: DisplayDecorationListener) {
+ if (DEBUG) Log.d(TAG, "registerDisplayDecorationListener")
+ mDisplayDecorationListeners.add(listener)
+ }
+
+ fun unregisterDisplayDecorationListener(listener: DisplayDecorationListener) {
+ if (DEBUG) Log.d(TAG, "unregisterDisplayDecorationListener")
+ mDisplayDecorationListeners.remove(listener)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 1c7f23c25d..cf0a3d570f 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -59,6 +59,7 @@ import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.Locale;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
public static final boolean SHELL_TRANSITIONS_ROTATION =
@@ -77,6 +78,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
private boolean mRecentsAnimationStartPending = false;
private boolean mShouldIgnoreMotionEvents = false;
+ private final int mDisplayId;
private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() {
@Override
@@ -101,10 +103,13 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
}
};
- TaskAnimationManager(Context ctx, RecentsAnimationDeviceState deviceState) {
+ public TaskAnimationManager(Context ctx, RecentsAnimationDeviceState deviceState,
+ int displayId) {
mCtx = ctx;
mDeviceState = deviceState;
+ mDisplayId = displayId;
}
+
SystemUiProxy getSystemUiProxy() {
return SystemUiProxy.INSTANCE.get(mCtx);
}
@@ -128,6 +133,17 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
public RecentsAnimationCallbacks startRecentsAnimation(@NonNull GestureState gestureState,
Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
ActiveGestureProtoLogProxy.logStartRecentsAnimation();
+ // Check displayId
+ if (mDisplayId != gestureState.getDisplayId()) {
+ String msg = String.format(Locale.ENGLISH,
+ "Constructor displayId %d does not equal gestureState display id %d",
+ mDisplayId, gestureState.getDisplayId());
+ if (FeatureFlags.IS_STUDIO_BUILD) {
+ throw new IllegalArgumentException(msg);
+ } else {
+ Log.e("TaskAnimationManager", msg, new Exception());
+ }
+ }
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
@@ -235,11 +251,11 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
BaseContainerInterface containerInterface =
mLastGestureState.getContainerInterface();
-
for (RemoteAnimationTarget compat : appearedTaskTargets) {
if (compat.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME
&& containerInterface.getCreatedContainer() instanceof RecentsActivity
- && DisplayController.getNavigationMode(mCtx) != NO_BUTTON) {
+ && DisplayController.INSTANCE.get(mCtx).getInfoForDisplay(
+ mDisplayId).getNavigationMode() != NO_BUTTON) {
// The only time we get onTasksAppeared() in button navigation with a
// 3p launcher is if the user goes to overview first, and in this case we
// can immediately finish the transition
@@ -489,6 +505,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskAnimationManager:");
+ pw.println(prefix + "\tmDisplayId=" + mDisplayId);
if (enableHandleDelayedGestureCallbacks()) {
pw.println(prefix + "\tmRecentsAnimationStartPending=" + mRecentsAnimationStartPending);
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index df66a5e30e..5bf4451fad 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -118,6 +118,7 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
TaskShortcutFactory.FREE_FORM,
DesktopSystemShortcut.Companion.createFactory(),
ExternalDisplaySystemShortcut.Companion.createFactory(),
+ AspectRatioSystemShortcut.Companion.createFactory(),
TaskShortcutFactory.WELLBEING,
TaskShortcutFactory.SAVE_APP_PAIR,
TaskShortcutFactory.SCREENSHOT,
@@ -202,10 +203,14 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
*/
public void initOverlay(Task task, @Nullable Bitmap thumbnail, Matrix matrix,
boolean rotated) {
- getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
+ if (!enableRefactorTaskThumbnail()) {
+ getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
+ }
if (thumbnail != null) {
- getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
+ if (!enableRefactorTaskThumbnail()) {
+ getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
+ }
getActionsView().setCallbacks(new OverlayUICallbacksImpl(isRealSnapshot(), task));
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 855ff98062..d161d452b9 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -433,9 +433,7 @@ public final class TaskViewUtils {
out.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
- for (RemoteTargetHandle remoteTargetHandle : remoteTargetHandles) {
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
- }
+ recentsView.setDrawBelowRecents(false, remoteTargetHandles);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 8116a8867d..43e8ff9902 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -22,14 +22,20 @@ import static android.content.Intent.ACTION_CHOOSER;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_A;
+import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableOverviewOnConnectedDisplays;
import static com.android.wm.shell.Flags.enableShellTopTaskTracking;
import static com.android.wm.shell.Flags.enableFlexibleSplit;
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT;
+import static com.android.launcher3.statehandlers.DesktopVisibilityController.INACTIVE_DESK_ID;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
+import android.app.WindowConfiguration;
+import android.content.Context;
import android.util.ArrayMap;
import android.util.Log;
@@ -37,6 +43,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.DaggerSingletonTracker;
@@ -47,8 +54,7 @@ import com.android.launcher3.util.SplitConfigurationOptions.StageType;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import com.android.quickstep.util.DesksUtils;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.quickstep.util.ExternalDisplaysKt;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -89,8 +95,11 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
// most.
private ArrayMap<Integer, GroupedTaskInfo> mVisibleTasks = new ArrayMap<>();
+ private final boolean mCanEnterDesktopMode;
+
@Inject
- public TopTaskTracker(DaggerSingletonTracker tracker, SystemUiProxy systemUiProxy) {
+ public TopTaskTracker(@ApplicationContext Context context, DaggerSingletonTracker tracker,
+ SystemUiProxy systemUiProxy) {
if (!enableShellTopTaskTracking()) {
mMainStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_MAIN;
mSideStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_SIDE;
@@ -107,6 +116,8 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this);
systemUiProxy.unregisterSplitScreenListener(this);
});
+
+ mCanEnterDesktopMode = canEnterDesktopMode(context);
}
@Override
@@ -316,21 +327,26 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
*/
@NonNull
@UiThread
- public CachedTaskInfo getCachedTopTask(boolean filterOnlyVisibleRecents) {
+ public CachedTaskInfo getCachedTopTask(boolean filterOnlyVisibleRecents, int displayId) {
if (enableShellTopTaskTracking()) {
// TODO(346588978): Currently ignore filterOnlyVisibleRecents, but perhaps make this an
// explicit filter For things to ignore (ie. PIP/Bubbles/Assistant/etc/so that this is
// explicit)
- // TODO(346588978): This assumes default display as gesture nav is only supported there
- return new CachedTaskInfo(mVisibleTasks.get(DEFAULT_DISPLAY));
+ return new CachedTaskInfo(mVisibleTasks.get(displayId), mCanEnterDesktopMode);
} else {
if (filterOnlyVisibleRecents) {
// Since we only know about the top most task, any filtering may not be applied on
// the cache. The second to top task may change while the top task is still the
// same.
- RunningTaskInfo[] tasks = TraceHelper.allowIpcs("getCachedTopTask.true", () ->
+ TaskInfo[] tasks = TraceHelper.allowIpcs("getCachedTopTask.true", () ->
ActivityManagerWrapper.getInstance().getRunningTasks(true));
- return new CachedTaskInfo(Arrays.asList(tasks));
+ if (enableOverviewOnConnectedDisplays()) {
+ return new CachedTaskInfo(Arrays.stream(tasks).filter(
+ info -> ExternalDisplaysKt.getSafeDisplayId(info)
+ == displayId).toList(), mCanEnterDesktopMode);
+ } else {
+ return new CachedTaskInfo(Arrays.asList(tasks), mCanEnterDesktopMode);
+ }
}
if (mOrderedTaskList.isEmpty()) {
@@ -344,7 +360,13 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
// Strip the pinned task and recents task
tasks.removeIf(t -> t.taskId == mPinnedTaskId || isRecentsTask(t)
|| DesksUtils.isDesktopWallpaperTask(t));
- return new CachedTaskInfo(tasks);
+ if (enableOverviewOnConnectedDisplays()) {
+ return new CachedTaskInfo(tasks.stream().filter(
+ info -> ExternalDisplaysKt.getSafeDisplayId(info) == displayId).toList(),
+ mCanEnterDesktopMode);
+ } else {
+ return new CachedTaskInfo(tasks, mCanEnterDesktopMode);
+ }
}
}
@@ -363,6 +385,8 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
* during the lifecycle of the task.
*/
public static class CachedTaskInfo {
+ // TODO: b/402362465 - Provide a valid value while tracking top task per display.
+ private final int mDisplayId = DEFAULT_DISPLAY;
// Only used when enableShellTopTaskTracking() is disabled
@Nullable
private final TaskInfo mTopTask;
@@ -373,20 +397,22 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
@Nullable
private final GroupedTaskInfo mVisibleTasks;
+ private final boolean mCanEnterDesktopMode;
// Only used when enableShellTopTaskTracking() is enabled
- CachedTaskInfo(@Nullable GroupedTaskInfo visibleTasks) {
+ CachedTaskInfo(@Nullable GroupedTaskInfo visibleTasks, boolean canEnterDesktopMode) {
mAllCachedTasks = null;
mTopTask = null;
mVisibleTasks = visibleTasks;
-
+ mCanEnterDesktopMode = canEnterDesktopMode;
}
// Only used when enableShellTopTaskTracking() is disabled
- CachedTaskInfo(@NonNull List<TaskInfo> allCachedTasks) {
+ CachedTaskInfo(@NonNull List<TaskInfo> allCachedTasks, boolean canEnterDesktopMode) {
mVisibleTasks = null;
mAllCachedTasks = allCachedTasks;
mTopTask = allCachedTasks.isEmpty() ? null : allCachedTasks.get(0);
+ mCanEnterDesktopMode = canEnterDesktopMode;
}
/**
@@ -496,58 +522,73 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
&& t.getActivityType() != ACTIVITY_TYPE_RECENTS)
.toList();
return visibleNonExcludedTasks.isEmpty() ? null
- : new CachedTaskInfo(visibleNonExcludedTasks);
+ : new CachedTaskInfo(visibleNonExcludedTasks, mCanEnterDesktopMode);
}
/**
- * Returns {@link Task} array which can be used as a placeholder until the true object
- * is loaded by the model
+ * Returns {@link TaskInfo} array corresponding to the provided task ids which can be
+ * used as a placeholder until the true object is loaded by the model. Only used when
+ * enableShellTopTaskTracking() is disabled.
*/
- public Task[] getPlaceholderTasks() {
- final TaskInfo baseTask = getLegacyBaseTask();
- // TODO(346588978): Update this to return more than a single task once the callers
- // are refactored
- return baseTask == null
- ? new Task[0]
- : new Task[]{Task.from(new TaskKey(baseTask), baseTask, false)};
+ private TaskInfo[] getSplitPlaceholderTasksInfo(int[] splitTaskIds) {
+ if (mTopTask == null) {
+ return new TaskInfo[0];
+ }
+ TaskInfo[] result = new TaskInfo[splitTaskIds.length];
+ for (int i = 0; i < splitTaskIds.length; i++) {
+ final int index = i;
+ int taskId = splitTaskIds[i];
+ mAllCachedTasks.forEach(rti -> {
+ if (rti.taskId == taskId) {
+ result[index] = rti;
+ }
+ });
+ }
+ return result;
}
+ private boolean isDesktopTask(TaskInfo taskInfo) {
+ return mCanEnterDesktopMode
+ && taskInfo.configuration.windowConfiguration.getWindowingMode()
+ == WindowConfiguration.WINDOWING_MODE_FREEFORM;
+ }
+
+ // TODO(346588978): Update this to return more than a single task once the callers
+ // are refactored.
+
/**
- * Returns {@link Task} array corresponding to the provided task ids which can be used as a
- * placeholder until the true object is loaded by the model
+ * Returns a {@link GroupedTaskInfo} which can be used as a placeholder until the true
+ * object is loaded by the model.
+ *
+ * @param splitTaskIds provide if it is for split, which represents the task ids of the
+ * paired tasks. Otherwise, provide null.
*/
- public Task[] getSplitPlaceholderTasks(int[] taskIds) {
+ public GroupedTaskInfo getPlaceholderGroupedTaskInfo(@Nullable int[] splitTaskIds) {
if (enableShellTopTaskTracking()) {
- if (mVisibleTasks == null || !mVisibleTasks.isBaseType(TYPE_SPLIT)) {
- return new Task[0];
- }
-
- GroupedTaskInfo splitTask = mVisibleTasks.getBaseGroupedTask();
- Task[] result = new Task[taskIds.length];
- for (int i = 0; i < taskIds.length; i++) {
- TaskInfo info = splitTask.getTaskById(taskIds[i]);
- if (info == null) {
- Log.w(TAG, "Requested task (" + taskIds[i] + ") not found");
- return new Task[0];
- }
- result[i] = Task.from(new TaskKey(info), info, false);
+ if (mVisibleTasks == null) {
+ return null;
}
- return result;
+ return mVisibleTasks.getBaseGroupedTask();
} else {
- if (mTopTask == null) {
- return new Task[0];
+ final TaskInfo baseTaskInfo = getLegacyBaseTask();
+ if (baseTaskInfo == null) {
+ return null;
}
- Task[] result = new Task[taskIds.length];
- for (int i = 0; i < taskIds.length; i++) {
- final int index = i;
- int taskId = taskIds[i];
- mAllCachedTasks.forEach(rti -> {
- if (rti.taskId == taskId) {
- result[index] = Task.from(new TaskKey(rti), rti, false);
- }
- });
+ if (splitTaskIds != null && splitTaskIds.length >= 2) {
+ TaskInfo[] splitTasksInfo = getSplitPlaceholderTasksInfo(splitTaskIds);
+ if (splitTasksInfo[0] == null || splitTasksInfo[1] == null) {
+ return null;
+ }
+ return GroupedTaskInfo.forSplitTasks(splitTasksInfo[0],
+ splitTasksInfo[1], /* splitBounds = */ null);
+ } else if (isDesktopTask(baseTaskInfo)) {
+ return GroupedTaskInfo.forDeskTasks(INACTIVE_DESK_ID, mDisplayId,
+ Collections.singletonList(
+ baseTaskInfo), /* minimizedFreeformTaskIds = */
+ Collections.emptySet());
+ } else {
+ return GroupedTaskInfo.forFullscreenTasks(baseTaskInfo);
}
- return result;
}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 741ae7dad5..30936ad9d4 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -35,6 +35,7 @@ import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WI
import static com.android.quickstep.GestureState.DEFAULT_STATE;
import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType;
import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER;
+import static com.android.quickstep.InputConsumer.createNoOpInputConsumer;
import static com.android.quickstep.InputConsumerUtils.newConsumer;
import static com.android.quickstep.InputConsumerUtils.tryCreateAssistantInputConsumer;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -94,11 +95,9 @@ import com.android.quickstep.OverviewCommandHelper.CommandType;
import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource;
-import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
-import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureLog.CompoundString;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -315,16 +314,15 @@ public class TouchInteractionService extends Service {
@BinderThread
@Override
public void onDisplayAddSystemDecorations(int displayId) {
- executeForTaskbarManager(taskbarManager ->
- taskbarManager.onDisplayAddSystemDecorations(displayId));
+ executeForTouchInteractionService(tis ->
+ tis.mSystemDecorationChangeObserver.notifyAddSystemDecorations(displayId));
}
@BinderThread
@Override
public void onDisplayRemoved(int displayId) {
- executeForTaskbarManager(taskbarManager ->
- taskbarManager.onDisplayRemoved(displayId));
executeForTouchInteractionService(tis -> {
+ tis.mSystemDecorationChangeObserver.notifyOnDisplayRemoved(displayId);
tis.mDeviceState.clearSysUIStateFlagsForDisplay(displayId);
});
}
@@ -332,8 +330,9 @@ public class TouchInteractionService extends Service {
@BinderThread
@Override
public void onDisplayRemoveSystemDecorations(int displayId) {
- executeForTaskbarManager(taskbarManager ->
- taskbarManager.onDisplayRemoveSystemDecorations(displayId));
+ executeForTouchInteractionService(tis -> {
+ tis.mSystemDecorationChangeObserver.notifyDisplayRemoveSystemDecorations(displayId);
+ });
}
@BinderThread
@@ -557,12 +556,11 @@ public class TouchInteractionService extends Service {
private OverviewComponentObserver mOverviewComponentObserver;
private InputConsumerController mInputConsumer;
private RecentsAnimationDeviceState mDeviceState;
- private TaskAnimationManager mTaskAnimationManager;
- private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
- private @NonNull InputConsumer mConsumer = InputConsumer.NO_OP;
+ private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.DEFAULT_NO_OP;
+ private @NonNull InputConsumer mConsumer = InputConsumer.DEFAULT_NO_OP;
private Choreographer mMainChoreographer;
- private @Nullable ResetGestureInputConsumer mResetGestureInputConsumer;
+ private boolean mUserUnlocked = false;
private GestureState mGestureState = DEFAULT_STATE;
private InputMonitorDisplayModel mInputMonitorDisplayModel;
@@ -582,6 +580,8 @@ public class TouchInteractionService extends Service {
private RecentsDisplayModel mRecentsDisplayModel;
+ private SystemDecorationChangeObserver mSystemDecorationChangeObserver;
+
@Override
public void onCreate() {
super.onCreate();
@@ -593,6 +593,7 @@ public class TouchInteractionService extends Service {
mDeviceState = RecentsAnimationDeviceState.INSTANCE.get(this);
mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(this);
mRecentsDisplayModel = RecentsDisplayModel.getINSTANCE().get(this);
+ mSystemDecorationChangeObserver = SystemDecorationChangeObserver.getINSTANCE().get(this);
mAllAppsActionManager = new AllAppsActionManager(
this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
mTrackpadsConnected = new ActiveTrackpadList(this, () -> {
@@ -667,7 +668,7 @@ public class TouchInteractionService extends Service {
if (ENABLE_GESTURE_NAV_ON_CONNECTED_DISPLAYS.isTrue()) {
mInputMonitorDisplayModel = new InputMonitorDisplayModel(this);
} else {
- mInputMonitorCompat = new InputMonitorCompat("swipe-up", Display.DEFAULT_DISPLAY);
+ mInputMonitorCompat = new InputMonitorCompat("swipe-up", DEFAULT_DISPLAY);
mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
mMainChoreographer, this::onInputEvent);
}
@@ -687,13 +688,11 @@ public class TouchInteractionService extends Service {
public void onUserUnlocked() {
Log.d(TAG, "onUserUnlocked: userId=" + getUserId()
+ " instance=" + System.identityHashCode(this));
- mTaskAnimationManager = new TaskAnimationManager(this, mDeviceState);
mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(this);
mOverviewCommandHelper = new OverviewCommandHelper(this,
- mOverviewComponentObserver, mTaskAnimationManager, mRecentsDisplayModel,
+ mOverviewComponentObserver, mRecentsDisplayModel,
SystemUiProxy.INSTANCE.get(this).getFocusState(), mTaskbarManager);
- mResetGestureInputConsumer = new ResetGestureInputConsumer(
- mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
+ mUserUnlocked = true;
mInputConsumer.registerInputConsumer();
for (int displayId : mDeviceState.getDisplaysWithSysUIState()) {
onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags(displayId), displayId);
@@ -766,14 +765,18 @@ public class TouchInteractionService extends Service {
if (LockedUserState.get(this).isUserUnlocked()) {
long systemUiStateFlags = mDeviceState.getSystemUiStateFlags(displayId);
mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags, displayId);
- if (displayId == Display.DEFAULT_DISPLAY) {
+ if (displayId == DEFAULT_DISPLAY) {
// The following don't care about non-default displays, at least for now. If they
// ever will, they should be taken care of.
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.setHomeDisabled(mDeviceState.isHomeDisabled());
// TODO b/399371607 - Propagate to taskAnimationManager once overview is multi
// display.
- mTaskAnimationManager.onSystemUiFlagsChanged(lastSysUIFlags, systemUiStateFlags);
+ TaskAnimationManager taskAnimationManager =
+ mRecentsDisplayModel.getTaskAnimationManager(displayId);
+ if (taskAnimationManager != null) {
+ taskAnimationManager.onSystemUiFlagsChanged(lastSysUIFlags, systemUiStateFlags);
+ }
}
}
}
@@ -865,11 +868,18 @@ public class TouchInteractionService extends Service {
boolean isHoverActionWithoutConsumer = enableCursorHoverStates()
&& isHoverActionWithoutConsumer(event);
+ TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager(
+ displayId);
+ if (taskAnimationManager == null) {
+ Log.e(TAG, "TaskAnimationManager not available for displayId " + displayId);
+ ActiveGestureProtoLogProxy.logOnTaskAnimationManagerNotAvailable(displayId);
+ return;
+ }
if (enableHandleDelayedGestureCallbacks()) {
if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
- mTaskAnimationManager.notifyNewGestureStart();
+ taskAnimationManager.notifyNewGestureStart();
}
- if (mTaskAnimationManager.shouldIgnoreMotionEvents()) {
+ if (taskAnimationManager.shouldIgnoreMotionEvents()) {
if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
ActiveGestureProtoLogProxy.logOnInputIgnoringFollowingEvents(displayId);
}
@@ -917,7 +927,7 @@ public class TouchInteractionService extends Service {
} else {
reasonString.append(" but event cannot trigger Assistant, "
+ "consuming gesture as no-op");
- mUncheckedConsumer = InputConsumer.NO_OP;
+ mUncheckedConsumer = createNoOpInputConsumer(displayId);
}
} else if ((!isOneHandedModeActive && isInSwipeUpTouchRegion)
|| isHoverActionWithoutConsumer || isOnBubbles) {
@@ -934,14 +944,14 @@ public class TouchInteractionService extends Service {
mGestureState = newGestureState;
mConsumer = newConsumer(
this,
- mResetGestureInputConsumer,
+ mUserUnlocked,
mOverviewComponentObserver,
mDeviceState,
prevGestureState,
mGestureState,
- mTaskAnimationManager,
+ taskAnimationManager,
inputMonitorCompat,
- getSwipeUpHandlerFactory(),
+ getSwipeUpHandlerFactory(displayId),
this::onConsumerInactive,
inputEventReceiver,
mTaskbarManager,
@@ -968,19 +978,22 @@ public class TouchInteractionService extends Service {
+ "consuming gesture for one-handed action");
// Consume gesture event for triggering one handed feature.
mUncheckedConsumer = new OneHandedModeInputConsumer(
- this, displayId, mDeviceState, InputConsumer.NO_OP, inputMonitorCompat);
+ this,
+ displayId,
+ mDeviceState,
+ InputConsumer.createNoOpInputConsumer(displayId), inputMonitorCompat);
} else {
- mUncheckedConsumer = InputConsumer.NO_OP;
+ mUncheckedConsumer = InputConsumer.createNoOpInputConsumer(displayId);
}
} else {
// Other events
- if (mUncheckedConsumer != InputConsumer.NO_OP) {
+ if (mUncheckedConsumer.getType() != InputConsumer.TYPE_NO_OP) {
// Only transform the event if we are handling it in a proper consumer
mRotationTouchHelper.setOrientationTransformIfNeeded(event);
}
}
- if (mUncheckedConsumer != InputConsumer.NO_OP) {
+ if (mUncheckedConsumer.getType() != InputConsumer.TYPE_NO_OP) {
switch (action) {
case ACTION_DOWN:
ActiveGestureProtoLogProxy.logOnInputEventActionDown(displayId, reasonString);
@@ -1035,7 +1048,8 @@ public class TouchInteractionService extends Service {
private boolean isHoverActionWithoutConsumer(MotionEvent event) {
// Only process these events when taskbar is present.
- TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
+ int displayId = event.getDisplayId();
+ TaskbarActivityContext tac = mTaskbarManager.getTaskbarForDisplay(displayId);
boolean isTaskbarPresent = tac != null && tac.getDeviceProfile().isTaskbarPresent
&& !tac.isPhoneMode();
return event.isHoverEvent() && (mUncheckedConsumer.getType() & TYPE_CURSOR_HOVER) == 0
@@ -1053,14 +1067,16 @@ public class TouchInteractionService extends Service {
GestureState.TrackpadGestureType trackpadGestureType) {
final GestureState gestureState;
TopTaskTracker.CachedTaskInfo taskInfo;
- if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+ TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager(
+ displayId);
+ if (taskAnimationManager != null && taskAnimationManager.isRecentsAnimationRunning()) {
gestureState = new GestureState(
mOverviewComponentObserver, displayId, ActiveGestureLog.INSTANCE.getLogId());
TopTaskTracker.CachedTaskInfo previousTaskInfo = previousGestureState.getRunningTask();
// previousTaskInfo can be null iff previousGestureState == GestureState.DEFAULT_STATE
taskInfo = previousTaskInfo != null
? previousTaskInfo
- : TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
+ : TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false, displayId);
gestureState.updateRunningTask(taskInfo);
gestureState.updateLastStartedTaskIds(previousGestureState.getLastStartedTaskIds());
gestureState.updatePreviouslyAppearedTaskIds(
@@ -1070,7 +1086,7 @@ public class TouchInteractionService extends Service {
mOverviewComponentObserver,
displayId,
ActiveGestureLog.INSTANCE.incrementLogId());
- taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
+ taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false, displayId);
gestureState.updateRunningTask(taskInfo);
}
gestureState.setTrackpadGestureType(trackpadGestureType);
@@ -1081,11 +1097,20 @@ public class TouchInteractionService extends Service {
return gestureState;
}
- public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
- return mOverviewComponentObserver.isHomeAndOverviewSame()
- ? mLauncherSwipeHandlerFactory
- : (RecentsWindowFlags.Companion.getEnableOverviewInWindow()
- ? mRecentsWindowSwipeHandlerFactory : mFallbackSwipeHandlerFactory);
+ /**
+ * Returns a AbsSwipeUpHandler.Factory, used to instantiate AbsSwipeUpHandler later.
+ * @param displayId The displayId of the display this handler will be used on.
+ */
+ public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory(int displayId) {
+ BaseContainerInterface<?, ?> containerInterface =
+ mOverviewComponentObserver.getContainerInterface(displayId);
+ if (containerInterface instanceof FallbackWindowInterface) {
+ return mRecentsWindowSwipeHandlerFactory;
+ } else if (containerInterface instanceof LauncherActivityInterface) {
+ return mLauncherSwipeHandlerFactory;
+ } else {
+ return mFallbackSwipeHandlerFactory;
+ }
}
/**
@@ -1100,7 +1125,12 @@ public class TouchInteractionService extends Service {
}
private void reset(int displayId) {
- mConsumer = mUncheckedConsumer = getDefaultInputConsumer();
+ mConsumer = mUncheckedConsumer = InputConsumerUtils.getDefaultInputConsumer(
+ displayId,
+ mUserUnlocked,
+ mRecentsDisplayModel.getTaskAnimationManager(displayId),
+ mTaskbarManager,
+ CompoundString.NO_OP);
mGestureState = DEFAULT_STATE;
// By default, use batching of the input events, but check receiver before using in the rare
// case that the monitor was disposed before the swipe settled
@@ -1110,29 +1140,6 @@ public class TouchInteractionService extends Service {
}
}
- private @NonNull InputConsumer getDefaultInputConsumer() {
- return getDefaultInputConsumer(CompoundString.NO_OP);
- }
-
- /**
- * Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP.
- */
- private @NonNull InputConsumer getDefaultInputConsumer(@NonNull CompoundString reasonString) {
- if (mResetGestureInputConsumer != null) {
- reasonString.append(
- "%smResetGestureInputConsumer initialized, using ResetGestureInputConsumer",
- SUBSTRING_PREFIX);
- return mResetGestureInputConsumer;
- } else {
- reasonString.append(
- "%smResetGestureInputConsumer not initialized, using no-op input consumer",
- SUBSTRING_PREFIX);
- // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
- // NO_OP until then (we never want these to be null).
- return InputConsumer.NO_OP;
- }
- }
-
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (!LockedUserState.get(this).isUserUnlocked()) {
@@ -1208,13 +1215,11 @@ public class TouchInteractionService extends Service {
if (createdOverviewContainer != null) {
createdOverviewContainer.getDeviceProfile().dump(this, "", pw);
}
+ resource.getTaskAnimationManager().dump("\t", pw);
}
pw.println("\tmConsumer=" + mConsumer.getName());
ActiveGestureLog.INSTANCE.dump("", pw);
RecentsModel.INSTANCE.get(this).dump("", pw);
- if (mTaskAnimationManager != null) {
- mTaskAnimationManager.dump("", pw);
- }
mTaskbarManager.dumpLogs("", pw);
DesktopVisibilityController.INSTANCE.get(this).dumpLogs("", pw);
pw.println("ContextualSearchStateManager:");
@@ -1226,22 +1231,28 @@ public class TouchInteractionService extends Service {
private AbsSwipeUpHandler createLauncherSwipeHandler(
GestureState gestureState, long touchTimeMs) {
- return new LauncherSwipeHandlerV2(this, mTaskAnimationManager,
- gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+ TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager(
+ gestureState.getDisplayId());
+ return new LauncherSwipeHandlerV2(this, taskAnimationManager,
+ gestureState, touchTimeMs, taskAnimationManager.isRecentsAnimationRunning(),
mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
}
private AbsSwipeUpHandler createFallbackSwipeHandler(
GestureState gestureState, long touchTimeMs) {
- return new FallbackSwipeHandler(this, mTaskAnimationManager,
- gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+ TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager(
+ gestureState.getDisplayId());
+ return new FallbackSwipeHandler(this, taskAnimationManager,
+ gestureState, touchTimeMs, taskAnimationManager.isRecentsAnimationRunning(),
mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
}
private AbsSwipeUpHandler createRecentsWindowSwipeHandler(
GestureState gestureState, long touchTimeMs) {
- return new RecentsWindowSwipeHandler(this, mTaskAnimationManager,
- gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+ TaskAnimationManager taskAnimationManager = mRecentsDisplayModel.getTaskAnimationManager(
+ gestureState.getDisplayId());
+ return new RecentsWindowSwipeHandler(this, taskAnimationManager,
+ gestureState, touchTimeMs, taskAnimationManager.isRecentsAnimationRunning(),
mInputConsumer, MSDLPlayerWrapper.INSTANCE.get(this));
}
@@ -1252,7 +1263,7 @@ public class TouchInteractionService extends Service {
private InputMonitorDisplayModel(Context context) {
super(context);
- registerDisplayListener();
+ initializeDisplays();
}
@NonNull
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
index d79a8eaca1..23b8a82b2a 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
@@ -25,6 +25,7 @@ import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.SimpleOrientationTouchTransformer;
+import com.android.quickstep.SystemDecorationChangeObserver;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
@@ -71,4 +72,5 @@ public interface QuickstepBaseAppComponent extends LauncherBaseAppComponent {
SimpleOrientationTouchTransformer getSimpleOrientationTouchTransformer();
+ SystemDecorationChangeObserver getSystemDecorationChangeObserver();
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 554cea262f..7db1813a90 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -19,6 +19,7 @@ import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.app.animation.Interpolators.INSTANT;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Flags.enableDesktopExplodedView;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.Flags.enableLargeDesktopWindowingTile;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
@@ -118,7 +119,9 @@ public class FallbackRecentsStateController implements StateHandler<RecentsState
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
setter.setFloat(mRecentsView, TASK_MODALNESS, state.getOverviewModalness(),
- config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
+ config.getInterpolator(ANIM_OVERVIEW_MODAL,
+ enableGridOnlyOverview() && !state.isRecentsViewVisible() ? FINAL_FRAME
+ : LINEAR));
setter.setFloat(mRecentsView, FULLSCREEN_PROGRESS, state.isFullScreen() ? 1 : 0, LINEAR);
boolean showAsGrid =
state.displayOverviewTasksAsGrid(mRecentsViewContainer.getDeviceProfile());
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 8ec97edbd7..dc1cdde4bc 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -17,6 +17,7 @@ package com.android.quickstep.fallback;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
@@ -55,6 +56,7 @@ import com.android.quickstep.views.RecentsViewContainer;
import com.android.quickstep.views.TaskContainer;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import java.util.ArrayList;
import java.util.Arrays;
@@ -115,11 +117,13 @@ public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewCon
* to the home task. This allows us to handle quick-switch similarly to a quick-switching
* from a foreground task.
*/
- public void onGestureAnimationStartOnHome(Task[] homeTask) {
+ public void onGestureAnimationStartOnHome(GroupedTaskInfo homeTaskInfo) {
// TODO(b/195607777) General fallback love, but this might be correct
// Home task should be defined as the front-most task info I think?
- mHomeTask = homeTask.length > 0 ? homeTask[0] : null;
- onGestureAnimationStart(homeTask);
+ if (homeTaskInfo != null) {
+ mHomeTask = Task.from(homeTaskInfo.getTaskInfo1());
+ }
+ onGestureAnimationStart(homeTaskInfo);
}
/**
@@ -174,13 +178,13 @@ public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewCon
}
@Override
- protected boolean shouldAddStubTaskView(Task[] runningTasks) {
- if (runningTasks.length > 1) {
+ protected boolean shouldAddStubTaskView(GroupedTaskInfo groupedTaskInfo) {
+ if (!groupedTaskInfo.isBaseType(GroupedTaskInfo.TYPE_FULLSCREEN)) {
// can't be in split screen w/ home task
- return super.shouldAddStubTaskView(runningTasks);
+ return super.shouldAddStubTaskView(groupedTaskInfo);
}
- Task runningTask = runningTasks[0];
+ Task runningTask = Task.from(groupedTaskInfo.getTaskInfo1());
if (mHomeTask != null && runningTask != null
&& mHomeTask.key.id == runningTask.key.id
&& !hasTaskViews() && mLoadPlanEverApplied) {
@@ -189,7 +193,7 @@ public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewCon
// Ignore empty task signal if applyLoadPlan has never run.
return false;
}
- return super.shouldAddStubTaskView(runningTasks);
+ return super.shouldAddStubTaskView(groupedTaskInfo);
}
@Override
@@ -252,7 +256,14 @@ public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewCon
@Override
public void onStateTransitionStart(RecentsState toState) {
setOverviewStateEnabled(true);
- setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile()));
+ if (enableGridOnlyOverview()) {
+ if (toState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile())) {
+ setOverviewGridEnabled(true);
+ }
+ } else {
+ setOverviewGridEnabled(
+ toState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile()));
+ }
setOverviewFullscreenEnabled(toState.isFullScreen());
if (toState == MODAL_TASK) {
setOverviewSelectEnabled(true);
@@ -271,6 +282,11 @@ public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewCon
@Override
public void onStateTransitionComplete(RecentsState finalState) {
DesktopVisibilityController.INSTANCE.get(mContainer).onLauncherStateChanged(finalState);
+ if (enableGridOnlyOverview()) {
+ if (!finalState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile())) {
+ setOverviewGridEnabled(false);
+ }
+ }
if (!finalState.isRecentsViewVisible()) {
// Clean-up logic that occurs when recents is no longer in use/visible.
reset();
@@ -292,8 +308,7 @@ public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewCon
// disabling this so app icons aren't drawn on top of recent tasks.
if (isOverlayEnabled && !RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
+ mBlurUtils.setDrawLiveTileBelowRecents(true);
}
}
@@ -303,6 +318,7 @@ public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewCon
if (enabled) {
RecentsState state = mContainer.getStateManager().getState();
setDisallowScrollToClearAll(!state.hasClearAllButton());
+ setDisallowScrollToAddDesk(!state.hasAddDeskButton());
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index 2c1a4eb186..77aac4176d 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -17,6 +17,7 @@ package com.android.quickstep.fallback;
import static com.android.launcher3.Flags.enableDesktopExplodedView;
import static com.android.launcher3.Flags.enableDesktopWindowingCarouselDetach;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState;
import static com.android.launcher3.uioverrides.states.OverviewModalTaskState.getOverviewScaleAndOffsetForModalState;
@@ -49,23 +50,31 @@ public class RecentsState implements BaseState<RecentsState> {
private static final int FLAG_ADD_DESK_BUTTON = BaseState.getFlag(10);
private static final int FLAG_SHOW_EXPLODED_DESKTOP_VIEW = BaseState.getFlag(11);
+ public static final int DEFAULT_STATE_ORDINAL = 0;
+ public static final int MODAL_TASK_ORDINAL = 1;
+ public static final int BACKGROUND_APP_ORDINAL = 2;
+ public static final int HOME_STATE_ORDINAL = 3;
+ public static final int BG_LAUNCHER_ORDINAL = 4;
+ public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 5;
+
private static final RecentsState[] sAllStates = new RecentsState[6];
- public static final RecentsState DEFAULT = new RecentsState(0,
+ public static final RecentsState DEFAULT = new RecentsState(DEFAULT_STATE_ORDINAL,
FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID
| FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE
| FLAG_ADD_DESK_BUTTON | FLAG_SHOW_EXPLODED_DESKTOP_VIEW);
- public static final RecentsState MODAL_TASK = new ModalState(1,
+ public static final RecentsState MODAL_TASK = new ModalState(MODAL_TASK_ORDINAL,
FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
| FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE
| FLAG_SHOW_EXPLODED_DESKTOP_VIEW);
- public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
+ public static final RecentsState BACKGROUND_APP = new BackgroundAppState(BACKGROUND_APP_ORDINAL,
FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN
| FLAG_RECENTS_VIEW_VISIBLE | FLAG_TASK_THUMBNAIL_SPLASH
| FLAG_DETACH_DESKTOP_CAROUSEL);
- public static final RecentsState HOME = new RecentsState(3, 0);
- public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
- public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5,
+ public static final RecentsState HOME = new RecentsState(HOME_STATE_ORDINAL, 0);
+ public static final RecentsState BG_LAUNCHER = new LauncherState(BG_LAUNCHER_ORDINAL, 0);
+ public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(
+ OVERVIEW_SPLIT_SELECT_ORDINAL,
FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_RECENTS_VIEW_VISIBLE | FLAG_CLOSE_POPUPS
| FLAG_DISABLE_RESTORE | FLAG_SHOW_EXPLODED_DESKTOP_VIEW);
@@ -89,7 +98,15 @@ public class RecentsState implements BaseState<RecentsState> {
@Override
public String toString() {
- return "Ordinal-" + ordinal;
+ return switch (ordinal) {
+ case DEFAULT_STATE_ORDINAL -> "DEFAULT";
+ case MODAL_TASK_ORDINAL -> "MODAL_TASK";
+ case BACKGROUND_APP_ORDINAL -> "BACKGROUND_APP";
+ case HOME_STATE_ORDINAL -> "HOME";
+ case BG_LAUNCHER_ORDINAL -> "BG_LAUNCHER";
+ case OVERVIEW_SPLIT_SELECT_ORDINAL -> "SPLIT_SELECT";
+ default -> "Unknown Ordinal-" + ordinal;
+ };
}
@Override
@@ -197,6 +214,9 @@ public class RecentsState implements BaseState<RecentsState> {
@Override
public float[] getOverviewScaleAndOffset(RecentsViewContainer container) {
+ if (enableGridOnlyOverview()) {
+ return super.getOverviewScaleAndOffset(container);
+ }
return getOverviewScaleAndOffsetForModalState(container.getOverviewPanel());
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsStateUtils.kt b/quickstep/src/com/android/quickstep/fallback/RecentsStateUtils.kt
new file mode 100644
index 0000000000..fec8a809b9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsStateUtils.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.fallback
+
+import com.android.launcher3.LauncherState
+
+fun RecentsState.toLauncherState(): LauncherState {
+ return when (ordinal) {
+ RecentsState.DEFAULT_STATE_ORDINAL -> LauncherState.OVERVIEW
+ RecentsState.MODAL_TASK_ORDINAL -> LauncherState.OVERVIEW_MODAL_TASK
+ RecentsState.BACKGROUND_APP_ORDINAL -> LauncherState.BACKGROUND_APP
+ RecentsState.HOME_STATE_ORDINAL -> LauncherState.NORMAL
+ RecentsState.BG_LAUNCHER_ORDINAL -> LauncherState.NORMAL
+ RecentsState.OVERVIEW_SPLIT_SELECT_ORDINAL -> LauncherState.OVERVIEW_SPLIT_SELECT
+ else -> LauncherState.NORMAL
+ }
+}
+
+fun RecentsState.toLauncherStateOrdinal(): Int = toLauncherState().ordinal
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
index 12dc17775f..5b88686cb8 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
@@ -17,7 +17,9 @@
package com.android.quickstep.fallback.window
import android.content.Context
+import android.util.Log
import android.view.Display
+import android.view.Display.DEFAULT_DISPLAY
import androidx.core.util.valueIterator
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppSingleton
@@ -26,6 +28,8 @@ import com.android.launcher3.util.DaggerSingletonTracker
import com.android.launcher3.util.WallpaperColorHints
import com.android.quickstep.DisplayModel
import com.android.quickstep.FallbackWindowInterface
+import com.android.quickstep.RecentsAnimationDeviceState
+import com.android.quickstep.TaskAnimationManager
import com.android.quickstep.dagger.QuickstepBaseAppComponent
import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource
import com.android.quickstep.fallback.window.RecentsWindowFlags.Companion.enableOverviewInWindow
@@ -54,9 +58,12 @@ constructor(
init {
if (enableOverviewInWindow) {
- registerDisplayListener()
- tracker.addCloseable { destroy() }
+ initializeDisplays()
+ } else {
+ // Always create resource for default display
+ storeDisplayResource(DEFAULT_DISPLAY)
}
+ tracker.addCloseable { destroy() }
}
override fun createDisplayResource(display: Display): RecentsDisplayResource {
@@ -68,13 +75,19 @@ constructor(
}
fun getRecentsWindowManager(displayId: Int): RecentsWindowManager? {
+ if (DEBUG) Log.d(TAG, "getRecentsWindowManager for display $displayId")
return getDisplayResource(displayId)?.recentsWindowManager
}
fun getFallbackWindowInterface(displayId: Int): FallbackWindowInterface? {
+ if (DEBUG) Log.d(TAG, "getFallbackWindowInterface for display $displayId")
return getDisplayResource(displayId)?.fallbackWindowInterface
}
+ fun getTaskAnimationManager(displayId: Int): TaskAnimationManager? {
+ return getDisplayResource(displayId)?.taskAnimationManager
+ }
+
val activeDisplayResources: Iterable<RecentsDisplayResource>
get() =
object : Iterable<RecentsDisplayResource> {
@@ -86,12 +99,20 @@ constructor(
val displayContext: Context,
val wallpaperColorHints: Int,
) : DisplayResource() {
- val recentsWindowManager = RecentsWindowManager(displayContext, wallpaperColorHints)
- val fallbackWindowInterface: FallbackWindowInterface =
- FallbackWindowInterface(recentsWindowManager)
+ val recentsWindowManager =
+ if (enableOverviewInWindow) RecentsWindowManager(displayContext, wallpaperColorHints)
+ else null
+ val fallbackWindowInterface =
+ if (enableOverviewInWindow) FallbackWindowInterface(recentsWindowManager) else null
+ val taskAnimationManager =
+ TaskAnimationManager(
+ displayContext,
+ RecentsAnimationDeviceState.INSTANCE.get(displayContext),
+ displayId,
+ )
override fun cleanup() {
- recentsWindowManager.destroy()
+ recentsWindowManager?.destroy()
}
override fun dump(prefix: String, writer: PrintWriter) {
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
index 99531546f5..d88077429e 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
@@ -29,8 +29,15 @@ class RecentsWindowFlags {
val enableFallbackOverviewInWindow: DesktopModeFlag =
DesktopModeFlag(Flags::enableFallbackOverviewInWindow, false)
+ @JvmField
+ val enableOverviewOnConnectedDisplays: DesktopModeFlag =
+ DesktopModeFlag(Flags::enableOverviewOnConnectedDisplays, false)
+
@JvmStatic
val enableOverviewInWindow
- get() = enableLauncherOverviewInWindow.isTrue || enableFallbackOverviewInWindow.isTrue
+ get() =
+ enableLauncherOverviewInWindow.isTrue ||
+ enableFallbackOverviewInWindow.isTrue ||
+ enableOverviewOnConnectedDisplays.isTrue
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
index 1a3a2e3dfe..bc08af2da6 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowManager.kt
@@ -44,9 +44,6 @@ import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory
import com.android.launcher3.statemanager.StatefulContainer
import com.android.launcher3.taskbar.TaskbarUIController
import com.android.launcher3.testing.TestLogging
-import com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL
-import com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL
-import com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL
import com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_MAIN
import com.android.launcher3.util.ContextTracker
import com.android.launcher3.util.DisplayController
@@ -70,8 +67,7 @@ import com.android.quickstep.fallback.RecentsState.BACKGROUND_APP
import com.android.quickstep.fallback.RecentsState.BG_LAUNCHER
import com.android.quickstep.fallback.RecentsState.DEFAULT
import com.android.quickstep.fallback.RecentsState.HOME
-import com.android.quickstep.fallback.RecentsState.MODAL_TASK
-import com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT
+import com.android.quickstep.fallback.toLauncherStateOrdinal
import com.android.quickstep.util.RecentsAtomicAnimationFactory
import com.android.quickstep.util.RecentsWindowProtoLogProxy
import com.android.quickstep.util.SplitSelectStateController
@@ -355,40 +351,16 @@ class RecentsWindowManager(context: Context, wallpaperColorHints: Int) :
override fun onStateSetStart(state: RecentsState) {
super.onStateSetStart(state)
- RecentsWindowProtoLogProxy.logOnStateSetStart(getStateName(state))
+ RecentsWindowProtoLogProxy.logOnStateSetStart(state.toString())
}
override fun onStateSetEnd(state: RecentsState) {
super.onStateSetEnd(state)
- RecentsWindowProtoLogProxy.logOnStateSetEnd(getStateName(state))
+ RecentsWindowProtoLogProxy.logOnStateSetEnd(state.toString())
if (state == HOME || state == BG_LAUNCHER) {
cleanupRecentsWindow()
}
- when (state) {
- HOME,
- BG_LAUNCHER ->
- AccessibilityManagerCompat.sendStateEventToTest(baseContext, NORMAL_STATE_ORDINAL)
- DEFAULT ->
- AccessibilityManagerCompat.sendStateEventToTest(baseContext, OVERVIEW_STATE_ORDINAL)
- OVERVIEW_SPLIT_SELECT ->
- AccessibilityManagerCompat.sendStateEventToTest(
- baseContext,
- OVERVIEW_SPLIT_SELECT_ORDINAL,
- )
- }
- }
-
- private fun getStateName(state: RecentsState?): String {
- return when (state) {
- null -> "NULL"
- DEFAULT -> "DEFAULT"
- MODAL_TASK -> "MODAL_TASK"
- BACKGROUND_APP -> "BACKGROUND_APP"
- HOME -> "HOME"
- BG_LAUNCHER -> "BG_LAUNCHER"
- OVERVIEW_SPLIT_SELECT -> "OVERVIEW_SPLIT_SELECT"
- else -> "ordinal=" + state.ordinal
- }
+ AccessibilityManagerCompat.sendStateEventToTest(baseContext, state.toLauncherStateOrdinal())
}
override fun getSystemUiController(): SystemUiController? {
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
index 1d85febad2..0d139b4e9f 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowSwipeHandler.java
@@ -222,6 +222,7 @@ public class RecentsWindowSwipeHandler extends AbsSwipeUpHandler<RecentsWindowMa
@Override
protected void finishRecentsControllerToHome(Runnable callback) {
final Runnable recentsCallback;
+ // TODO(b/404866791): check if this is actually necessary for this recents-in-window class
if (mAppCanEnterPip) {
// Make sure Launcher is resumed after auto-enter-pip transition to actually trigger
// the PiP task appearing.
@@ -238,7 +239,7 @@ public class RecentsWindowSwipeHandler extends AbsSwipeUpHandler<RecentsWindowMa
}
mRecentsView.cleanupRemoteTargets();
mRecentsAnimationController.finish(
- mAppCanEnterPip /* toRecents */, recentsCallback, true /* sendUserLeaveHint */);
+ true /* toRecents */, recentsCallback, true /* sendUserLeaveHint */);
}
@Override
@@ -256,7 +257,8 @@ public class RecentsWindowSwipeHandler extends AbsSwipeUpHandler<RecentsWindowMa
if (mRunningOverHome) {
if (DisplayController.getNavigationMode(mContext).hasGestures) {
mRecentsView.onGestureAnimationStartOnHome(
- mGestureState.getRunningTask().getPlaceholderTasks());
+ mGestureState.getRunningTask().getPlaceholderGroupedTaskInfo(
+ /* splitTaskIds = */ null));
}
} else {
super.notifyGestureAnimationStartToRecents();
@@ -282,7 +284,7 @@ public class RecentsWindowSwipeHandler extends AbsSwipeUpHandler<RecentsWindowMa
private final AnimatedFloat mHomeAlpha = new AnimatedFloat(this::updateAppTransforms);
private final AnimatedFloat mVerticalShiftForScale =
new AnimatedFloat(this::updateAppTransforms);
- private final AnimatedFloat mRecentsAlpha = new AnimatedFloat(this:: updateAppTransforms);
+ private final AnimatedFloat mRecentsAlpha = new AnimatedFloat(this::updateAppTransforms);
private final RectF mTargetRect = new RectF();
private SurfaceControl mSurfaceControl;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
index 107babd61e..571a5468d1 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java
@@ -88,7 +88,7 @@ public class NavHandleLongPressHandler implements ResourceBasedOverride {
*/
@Nullable
@VisibleForTesting
- final Runnable getLongPressRunnable(NavHandle navHandle) {
+ final Runnable getLongPressRunnable(NavHandle navHandle, int displayId) {
if (!isContextualSearchEntrypointEnabled(navHandle)) {
Log.i(TAG, "Contextual Search invocation failed: entry point disabled");
mVibratorWrapper.cancelVibrate();
@@ -116,7 +116,7 @@ public class NavHandleLongPressHandler implements ResourceBasedOverride {
Log.i(TAG, "Contextual Search invocation successful");
String runningPackage = TopTaskTracker.INSTANCE.get(mContext).getCachedTopTask(
- /* filterOnlyVisibleRecents */ true).getPackageName();
+ /* filterOnlyVisibleRecents */ true, displayId).getPackageName();
mStatsLogManager.logger().withPackageName(runningPackage)
.log(LAUNCHER_LAUNCH_ASSISTANT_SUCCESSFUL_NAV_HANDLE);
} else {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index baabde884e..af7c975e0b 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -214,7 +214,7 @@ public class NavHandleLongPressInputConsumer extends DelegateInputConsumer {
&& !mDeepPressLogged) {
// Log deep press even if feature is disabled.
String runningPackage = mTopTaskTracker.getCachedTopTask(
- /* filterOnlyVisibleRecents */ true).getPackageName();
+ /* filterOnlyVisibleRecents */ true, getDisplayId()).getPackageName();
mStatsLogManager.logger().withPackageName(runningPackage).log(
mNavHandle.isNavHandleStashedTaskbar() ? LAUNCHER_DEEP_PRESS_STASHED_TASKBAR
: LAUNCHER_DEEP_PRESS_NAVBAR);
@@ -233,12 +233,13 @@ public class NavHandleLongPressInputConsumer extends DelegateInputConsumer {
Log.d(TAG, "triggerLongPress");
}
String runningPackage = mTopTaskTracker.getCachedTopTask(
- /* filterOnlyVisibleRecents */ true).getPackageName();
+ /* filterOnlyVisibleRecents */ true, getDisplayId()).getPackageName();
mStatsLogManager.logger().withPackageName(runningPackage).log(
mNavHandle.isNavHandleStashedTaskbar() ? LAUNCHER_LONG_PRESS_STASHED_TASKBAR
: LAUNCHER_LONG_PRESS_NAVBAR);
- Runnable longPressRunnable = mNavHandleLongPressHandler.getLongPressRunnable(mNavHandle);
+ Runnable longPressRunnable = mNavHandleLongPressHandler.getLongPressRunnable(mNavHandle,
+ getDisplayId());
if (longPressRunnable == null) {
return;
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 2db75738f4..54cc391293 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -58,6 +58,7 @@ import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
@@ -424,6 +425,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mMotionPauseDetector.setIsTrackpadGesture(mGestureState.isTrackpadGesture());
mInteractionHandler.initWhenReady(
"OtherActivityInputConsumer.startTouchTrackingForWindowAnimation");
+ ActiveGestureProtoLogProxy.logGestureStartSwipeHandler(
+ mInteractionHandler.getClass().getSimpleName());
if (DEBUG) {
Log.d(TAG, "startTouchTrackingForWindowAnimation: isRecentsAnimationRunning="
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
deleted file mode 100644
index 349f4d2f2f..0000000000
--- a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.inputconsumers;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.quickstep.InputConsumer;
-import com.android.quickstep.TaskAnimationManager;
-
-import java.util.function.Supplier;
-
-/**
- * A NO_OP input consumer which also resets any pending gesture
- */
-public class ResetGestureInputConsumer implements InputConsumer {
-
- private final TaskAnimationManager mTaskAnimationManager;
- private final Supplier<TaskbarActivityContext> mActivityContextSupplier;
-
- public ResetGestureInputConsumer(
- TaskAnimationManager taskAnimationManager,
- Supplier<TaskbarActivityContext> activityContextSupplier) {
- mTaskAnimationManager = taskAnimationManager;
- mActivityContextSupplier = activityContextSupplier;
- }
-
- @Override
- public int getType() {
- return TYPE_RESET_GESTURE;
- }
-
- @Override
- public void onMotionEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN
- && mTaskAnimationManager.isRecentsAnimationRunning()) {
- TaskbarActivityContext tac = mActivityContextSupplier.get();
- mTaskAnimationManager.finishRunningRecentsAnimation(
- /* toHome= */ tac != null && !tac.isInApp());
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.kt b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.kt
new file mode 100644
index 0000000000..96e7943061
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.inputconsumers
+
+import android.view.MotionEvent
+import com.android.launcher3.taskbar.TaskbarActivityContext
+import com.android.quickstep.InputConsumer
+import com.android.quickstep.TaskAnimationManager
+import java.util.function.Supplier
+
+/** A NO_OP input consumer which also resets any pending gesture */
+class ResetGestureInputConsumer(
+ private val displayId: Int,
+ private val taskAnimationManager: TaskAnimationManager,
+ private val activityContextSupplier: Supplier<TaskbarActivityContext?>,
+) : InputConsumer {
+ override fun getType() = InputConsumer.TYPE_RESET_GESTURE
+
+ override fun getDisplayId() = displayId
+
+ override fun onMotionEvent(ev: MotionEvent) {
+ if (
+ ev.action == MotionEvent.ACTION_DOWN && taskAnimationManager.isRecentsAnimationRunning
+ ) {
+ val tac = activityContextSupplier.get()
+ taskAnimationManager.finishRunningRecentsAnimation(
+ /* toHome= */ tac != null && !tac.isInApp
+ )
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 5995ca25df..c7e2ade6d1 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -30,8 +30,11 @@ import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Rect;
+import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.RippleDrawable;
+import android.os.SystemProperties;
+import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -56,11 +59,12 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.views.ClipIconView;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import com.airbnb.lottie.LottieAnimationView;
@@ -80,6 +84,10 @@ abstract class TutorialController implements BackGestureAttemptCallback,
private static final String PIXEL_TIPS_APP_PACKAGE_NAME = "com.google.android.apps.tips";
private static final CharSequence DEFAULT_PIXEL_TIPS_APP_NAME = "Pixel Tips";
+ private static final String SUW_THEME_SYSTEM_PROPERTY = "setupwizard.theme";
+ private static final String GLIF_EXPRESSIVE_THEME = "glif_expressive";
+ private static final String GLIF_EXPRESSIVE_LIGHT_THEME = "glif_expressive_light";
+
private static final int FEEDBACK_ANIMATION_MS = 133;
private static final int SUBTITLE_ANNOUNCE_DELAY_MS = 3000;
private static final int DONE_BUTTON_ANNOUNCE_DELAY_MS = 4000;
@@ -115,6 +123,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
protected View mExitingAppView;
protected int mExitingAppRadius;
private final AlertDialog mSkipTutorialDialog;
+ private final boolean mIsExpressiveThemeEnabledInSUW;
private boolean mGestureCompleted = false;
protected LottieAnimationView mAnimatedGestureDemonstration;
@@ -172,7 +181,11 @@ abstract class TutorialController implements BackGestureAttemptCallback,
mFeedbackTitleView.setText(getIntroductionTitle());
mFeedbackSubtitleView.setText(getIntroductionSubtitle());
- setTitleTypefaces();
+
+ String SUWTheme = SystemProperties.get(SUW_THEME_SYSTEM_PROPERTY, "");
+ mIsExpressiveThemeEnabledInSUW = SUWTheme.equals(GLIF_EXPRESSIVE_THEME) || SUWTheme.equals(
+ GLIF_EXPRESSIVE_LIGHT_THEME);
+ maybeSetTitleTypefaces();
mExitingAppView.setClipToOutline(true);
mExitingAppView.setOutlineProvider(new ViewOutlineProvider() {
@@ -406,12 +419,21 @@ abstract class TutorialController implements BackGestureAttemptCallback,
mFeedbackTitleView.setText(titleResId);
mFeedbackSubtitleView.setText(subtitleResId);
+
+ boolean isUserSetupComplete = SettingsCache.INSTANCE.get(mContext).getValue(
+ Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
+ boolean userSetupNotCompleteAndExpressiveThemeEnabled =
+ !isUserSetupComplete && mIsExpressiveThemeEnabledInSUW;
+ boolean userSetupCompleteAndNewFontsEnabled = isUserSetupComplete && Flags.enableGsf();
+
if (isGestureSuccessful) {
if (mTutorialFragment.isAtFinalStep()) {
- TypefaceUtils.setTypeface(
- mDoneButton,
- FontFamily.GSF_LABEL_LARGE
- );
+ if (userSetupCompleteAndNewFontsEnabled
+ || userSetupNotCompleteAndExpressiveThemeEnabled) {
+ mDoneButton.setTypeface(
+ Typeface.create(FontFamily.GSF_LABEL_LARGE.getValue(),
+ Typeface.NORMAL));
+ }
showActionButton();
}
@@ -437,7 +459,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
mCheckmarkAnimation.setVisibility(View.VISIBLE);
mCheckmarkAnimation.playAnimation();
mFeedbackTitleView.setTextAppearance(getSuccessTitleTextAppearance());
- setTitleTypefaces();
+ maybeSetTitleTypefaces();
}
public boolean isGestureCompleted() {
@@ -492,7 +514,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
mFeedbackTitleView.setTextAppearance(getTitleTextAppearance());
mDoneButton.setTextAppearance(getDoneButtonTextAppearance());
- setTitleTypefaces();
+ maybeSetTitleTypefaces();
mDoneButton.getBackground().setTint(getDoneButtonColor());
mCheckmarkAnimation.setAnimation(mTutorialFragment.isAtFinalStep()
? R.raw.checkmark_animation_end
@@ -514,16 +536,15 @@ abstract class TutorialController implements BackGestureAttemptCallback,
/**
* Apply expressive typefaces to the feedback title and subtitle views.
*/
- private void setTitleTypefaces() {
- TypefaceUtils.setTypeface(
- mFeedbackTitleView,
- mTutorialFragment.isLargeScreen()
- ? FontFamily.GSF_DISPLAY_MEDIUM_EMPHASIZED
- : FontFamily.GSF_DISPLAY_SMALL_EMPHASIZED);
- TypefaceUtils.setTypeface(
- mFeedbackSubtitleView,
- FontFamily.GSF_BODY_LARGE
- );
+ private void maybeSetTitleTypefaces() {
+ if (mIsExpressiveThemeEnabledInSUW || Flags.enableGsf()) {
+ mFeedbackTitleView.setTypeface(Typeface.create(mTutorialFragment.isLargeScreen()
+ ? FontFamily.GSF_DISPLAY_MEDIUM_EMPHASIZED.getValue()
+ : FontFamily.GSF_DISPLAY_SMALL_EMPHASIZED.getValue(),
+ Typeface.NORMAL));
+ mFeedbackSubtitleView.setTypeface(
+ Typeface.create(FontFamily.GSF_BODY_LARGE.getValue(), Typeface.NORMAL));
+ }
}
protected void resetViewsForBackGesture() {
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index c4e343e1d0..4b9eb9e77f 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -275,7 +275,7 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {
desiredTaskId: Int,
banner: View,
): Pair<Float, Float> {
- val snapshotParams = thumbnailViews[0].layoutParams as LinearLayout.LayoutParams
+ val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
val translationX = banner.height.toFloat()
val translationY: Float
if (splitBounds == null) {
@@ -661,7 +661,7 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {
): SplitIconPositions {
return if (Flags.enableOverviewIconMenu()) {
if (isRtl) {
- SplitIconPositions(0, -(totalThumbnailHeight - primarySnapshotHeight))
+ SplitIconPositions(-(totalThumbnailHeight - primarySnapshotHeight), 0)
} else {
SplitIconPositions(0, primarySnapshotHeight + dividerSize)
}
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
index 15eb69e47a..74ae688bd7 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.kt
@@ -255,7 +255,7 @@ class PortraitPagedViewHandler : DefaultPagedViewHandler(), RecentsPagedOrientat
}
} else {
if (desiredTaskId == splitBounds.leftTopTaskId) {
- val snapshotParams = thumbnailViews[0].layoutParams as LinearLayout.LayoutParams
+ val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
val bottomRightTaskPlusDividerPercent =
(splitBounds.rightBottomTaskPercent + splitBounds.dividerPercent)
translationY =
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index 67358bbb45..456115fdca 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -26,7 +26,6 @@ import android.view.Surface
import android.view.View
import android.view.View.MeasureSpec
import android.widget.FrameLayout
-import android.widget.LinearLayout
import androidx.core.util.component1
import androidx.core.util.component2
import androidx.core.view.updateLayoutParams
@@ -152,7 +151,7 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {
desiredTaskId: Int,
banner: View,
): Pair<Float, Float> {
- val snapshotParams = thumbnailViews[0].layoutParams as LinearLayout.LayoutParams
+ val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
val translationX: Float = (taskViewWidth - banner.height).toFloat()
val translationY: Float
if (splitBounds == null) {
@@ -248,14 +247,14 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {
val iconCenter = iconAppChipView.getHeight() / 2f
if (isRtl) {
- iconMenuParams.gravity = Gravity.TOP or Gravity.END
+ iconMenuParams.gravity = Gravity.TOP or Gravity.START
iconMenuParams.topMargin = iconMenuMargin
iconMenuParams.marginEnd = thumbnailTopMargin
// Use half menu height to place the pivot within the X/Y center of icon in the menu.
iconAppChipView.pivotX = iconMenuParams.width / 2f
iconAppChipView.pivotY = iconMenuParams.width / 2f
} else {
- iconMenuParams.gravity = Gravity.BOTTOM or Gravity.START
+ iconMenuParams.gravity = Gravity.BOTTOM or Gravity.END
iconMenuParams.topMargin = 0
iconMenuParams.marginEnd = 0
iconAppChipView.pivotX = iconCenter
@@ -409,7 +408,7 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {
if (Flags.enableOverviewIconMenu()) {
val appChipView = iconView as IconAppChipView
layoutParams.gravity =
- if (isRtl) Gravity.TOP or Gravity.END else Gravity.BOTTOM or Gravity.START
+ if (isRtl) Gravity.TOP or Gravity.START else Gravity.BOTTOM or Gravity.END
appChipView.layoutParams = layoutParams
appChipView.setSplitTranslationX(0f)
appChipView.setSplitTranslationY(translationY.toFloat())
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index 553a620fd8..00ef13bf0d 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -31,7 +31,7 @@ import com.android.quickstep.recents.domain.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.domain.usecase.IsThumbnailValidUseCase
import com.android.quickstep.recents.domain.usecase.OrganizeDesktopTasksUseCase
import com.android.quickstep.recents.viewmodel.RecentsViewData
-import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper.PreviewPositionHelperFactory
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
@@ -203,7 +203,7 @@ class RecentsDependencies private constructor(appContext: Context) {
GetThumbnailPositionUseCase(
deviceProfileRepository = inject(scopeId),
rotationStateRepository = inject(scopeId),
- previewPositionHelper = PreviewPositionHelper(),
+ previewPositionHelperFactory = PreviewPositionHelperFactory(),
)
OrganizeDesktopTasksUseCase::class.java -> OrganizeDesktopTasksUseCase()
else -> {
diff --git a/quickstep/src/com/android/quickstep/recents/domain/usecase/GetThumbnailPositionUseCase.kt b/quickstep/src/com/android/quickstep/recents/domain/usecase/GetThumbnailPositionUseCase.kt
index 8501382f18..e83d9f0549 100644
--- a/quickstep/src/com/android/quickstep/recents/domain/usecase/GetThumbnailPositionUseCase.kt
+++ b/quickstep/src/com/android/quickstep/recents/domain/usecase/GetThumbnailPositionUseCase.kt
@@ -27,7 +27,7 @@ import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
class GetThumbnailPositionUseCase(
private val deviceProfileRepository: RecentsDeviceProfileRepository,
private val rotationStateRepository: RecentsRotationStateRepository,
- private val previewPositionHelper: PreviewPositionHelper,
+ private val previewPositionHelperFactory: PreviewPositionHelper.PreviewPositionHelperFactory,
) {
operator fun invoke(
thumbnailData: ThumbnailData?,
@@ -38,6 +38,7 @@ class GetThumbnailPositionUseCase(
val thumbnail =
thumbnailData?.thumbnail ?: return ThumbnailPosition(Matrix.IDENTITY_MATRIX, false)
+ val previewPositionHelper = previewPositionHelperFactory.create()
previewPositionHelper.updateThumbnailMatrix(
Rect(0, 0, thumbnail.width, thumbnail.height),
thumbnailData,
diff --git a/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt b/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
index 679daf8ef6..619075ff83 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
@@ -17,78 +17,67 @@
package com.android.quickstep.recents.ui.mapper
import android.view.View.OnClickListener
-import com.android.launcher3.Flags.enableDesktopExplodedView
import com.android.quickstep.recents.ui.viewmodel.TaskData
-import com.android.quickstep.task.thumbnail.TaskHeaderUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
object TaskUiStateMapper {
/**
- * Converts a [TaskData] object into a [TaskHeaderUiState] for display in the UI.
+ * Converts a [TaskData] object into a [TaskThumbnailUiState] for display in the UI.
*
* This function handles different types of [TaskData] and determines the appropriate UI state
* based on the data and provided flags.
*
* @param taskData The [TaskData] to convert. Can be null or a specific subclass.
+ * @param isLiveTile A flag indicating whether the task data represents live tile.
* @param hasHeader A flag indicating whether the UI should display a header.
* @param clickCloseListener A callback when the close button in the UI is clicked.
- * @return A [TaskHeaderUiState] representing the UI state for the given task data.
+ * @return A [TaskThumbnailUiState] representing the UI state for the given task data.
*/
- fun toTaskHeaderState(
+ fun toTaskThumbnailUiState(
taskData: TaskData?,
+ isLiveTile: Boolean,
hasHeader: Boolean,
clickCloseListener: OnClickListener?,
- ): TaskHeaderUiState =
- when {
- taskData !is TaskData.Data -> TaskHeaderUiState.HideHeader
- canHeaderBeCreated(taskData, hasHeader, clickCloseListener) -> {
- TaskHeaderUiState.ShowHeader(
- TaskHeaderUiState.ThumbnailHeader(
- // TODO(http://b/353965691): figure out what to do when `icon` or
- // `titleDescription` is null.
- taskData.icon!!,
- taskData.titleDescription!!,
- clickCloseListener!!,
- )
- )
- }
- else -> TaskHeaderUiState.HideHeader
- }
-
- /**
- * Converts a [TaskData] object into a [TaskThumbnailUiState] for display in the UI.
- *
- * This function handles different types of [TaskData] and determines the appropriate UI state
- * based on the data and provided flags.
- *
- * @param taskData The [TaskData] to convert. Can be null or a specific subclass.
- * @param isLiveTile A flag indicating whether the task data represents live tile.
- * @return A [TaskThumbnailUiState] representing the UI state for the given task data.
- */
- fun toTaskThumbnailUiState(taskData: TaskData?, isLiveTile: Boolean): TaskThumbnailUiState =
+ ): TaskThumbnailUiState =
when {
taskData !is TaskData.Data -> Uninitialized
- isLiveTile -> LiveTile
+ isLiveTile -> createLiveTileState(taskData, hasHeader, clickCloseListener)
isBackgroundOnly(taskData) -> BackgroundOnly(taskData.backgroundColor)
isSnapshotSplash(taskData) ->
SnapshotSplash(
- Snapshot(
- taskData.thumbnailData?.thumbnail!!,
- taskData.thumbnailData.rotation,
- taskData.backgroundColor,
- ),
+ createSnapshotState(taskData, hasHeader, clickCloseListener),
taskData.icon,
)
-
else -> Uninitialized
}
+ private fun createSnapshotState(
+ taskData: TaskData.Data,
+ hasHeader: Boolean,
+ clickCloseListener: OnClickListener?,
+ ): Snapshot =
+ if (canHeaderBeCreated(taskData, hasHeader, clickCloseListener)) {
+ Snapshot.WithHeader(
+ taskData.thumbnailData?.thumbnail!!,
+ taskData.thumbnailData.rotation,
+ taskData.backgroundColor,
+ ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!, clickCloseListener!!),
+ )
+ } else {
+ Snapshot.WithoutHeader(
+ taskData.thumbnailData?.thumbnail!!,
+ taskData.thumbnailData.rotation,
+ taskData.backgroundColor,
+ )
+ }
+
private fun isBackgroundOnly(taskData: TaskData.Data) =
taskData.isLocked || taskData.thumbnailData == null
@@ -100,9 +89,21 @@ object TaskUiStateMapper {
hasHeader: Boolean,
clickCloseListener: OnClickListener?,
) =
- enableDesktopExplodedView() &&
- hasHeader &&
+ hasHeader &&
taskData.icon != null &&
taskData.titleDescription != null &&
clickCloseListener != null
+
+ private fun createLiveTileState(
+ taskData: TaskData.Data,
+ hasHeader: Boolean,
+ clickCloseListener: OnClickListener?,
+ ) =
+ if (canHeaderBeCreated(taskData, hasHeader, clickCloseListener)) {
+ // TODO(http://b/353965691): figure out what to do when `icon` or `titleDescription` is
+ // null.
+ LiveTile.WithHeader(
+ ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!, clickCloseListener!!)
+ )
+ } else LiveTile.WithoutHeader
}
diff --git a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
index 8a6a8051f1..863d30b2e4 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskTileUiState.kt
@@ -37,6 +37,7 @@ data class TaskTileUiState(
val hasHeader: Boolean,
val sysUiStatusNavFlags: Int,
val taskOverlayEnabled: Boolean,
+ val isCentralTask: Boolean,
)
sealed class TaskData {
diff --git a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
index 09e20711d0..fac0c0a0bb 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModel.kt
@@ -29,7 +29,6 @@ import com.android.quickstep.recents.domain.usecase.IsThumbnailValidUseCase
import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
import com.android.quickstep.recents.viewmodel.RecentsViewData
import com.android.quickstep.views.TaskViewType
-import com.android.quickstep.views.TaskViewType.SINGLE
import com.android.systemui.shared.recents.model.ThumbnailData
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -66,6 +65,12 @@ class TaskViewModel(
}
.distinctUntilChanged()
+ private val isCentralTask =
+ combine(taskIds, recentsViewData.centralTaskIds) { taskIds, centralTaskIds ->
+ taskIds == centralTaskIds
+ }
+ .distinctUntilChanged()
+
private val taskData =
taskIds.flatMapLatest { ids ->
// Combine Tasks requests
@@ -79,14 +84,12 @@ class TaskViewModel(
combine(recentsViewData.overlayEnabled, recentsViewData.settledFullyVisibleTaskIds) {
isOverlayEnabled,
settledFullyVisibleTaskIds ->
- taskViewType == SINGLE &&
- isOverlayEnabled &&
- settledFullyVisibleTaskIds.any { it in taskIds.value }
+ isOverlayEnabled && settledFullyVisibleTaskIds.any { it in taskIds.value }
}
.distinctUntilChanged()
val state: Flow<TaskTileUiState> =
- combine(taskData, isLiveTile, overlayEnabled, ::mapToTaskTile)
+ combine(taskData, isLiveTile, overlayEnabled, isCentralTask, ::mapToTaskTile)
.distinctUntilChanged()
.flowOn(dispatcherProvider.background)
@@ -114,6 +117,7 @@ class TaskViewModel(
tasks: List<TaskData>,
isLiveTile: Boolean,
overlayEnabled: Boolean,
+ isCentralTask: Boolean,
): TaskTileUiState {
val firstThumbnailData = (tasks.firstOrNull() as? TaskData.Data)?.thumbnailData
return TaskTileUiState(
@@ -122,6 +126,7 @@ class TaskViewModel(
hasHeader = taskViewType == TaskViewType.DESKTOP,
sysUiStatusNavFlags = getSysUiStatusNavFlagsUseCase(firstThumbnailData),
taskOverlayEnabled = overlayEnabled,
+ isCentralTask = isCentralTask,
)
}
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
index 2465a4671b..803fb354ff 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt
@@ -27,6 +27,9 @@ class RecentsViewData {
// The settled set of visible taskIds that is updated after RecentsView scroll settles.
val settledFullyVisibleTaskIds = MutableStateFlow(emptySet<Int>())
+ // The id for the task ids in the TaskView that controls the Actions View
+ val centralTaskIds = MutableStateFlow(emptySet<Int>())
+
// A list of taskIds that are associated with a RecentsAnimationController. */
val runningTaskIds = MutableStateFlow(emptySet<Int>())
diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
index 5ff8aaa840..4d168c7476 100644
--- a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
+++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewModel.kt
@@ -38,6 +38,10 @@ class RecentsViewModel(
recentsViewData.settledFullyVisibleTaskIds.value = taskIds
}
+ fun updateCentralTaskIds(taskIds: Set<Int>) {
+ recentsViewData.centralTaskIds.value = taskIds
+ }
+
fun setOverlayEnabled(isOverlayEnabled: Boolean) {
recentsViewData.overlayEnabled.value = isOverlayEnabled
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt
deleted file mode 100644
index a40929c5f0..0000000000
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskContentView.kt
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.task.thumbnail
-
-import android.content.Context
-import android.graphics.Outline
-import android.graphics.Path
-import android.graphics.Rect
-import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewOutlineProvider
-import android.widget.LinearLayout
-import androidx.core.view.isInvisible
-import com.android.launcher3.Flags.enableRefactorTaskThumbnail
-import com.android.launcher3.R
-import com.android.launcher3.util.ViewPool
-import com.android.quickstep.views.TaskHeaderView
-import com.android.quickstep.views.TaskThumbnailViewDeprecated
-
-/**
- * TaskContentView is a wrapper around the TaskHeaderView and TaskThumbnailView. It is a sibling to
- * DWB, AiAi (TaskOverlay).
- */
-class TaskContentView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
- LinearLayout(context, attrs), ViewPool.Reusable {
-
- private var taskHeaderView: TaskHeaderView? = null
- private var taskThumbnailView: TaskThumbnailView? = null
- private var taskThumbnailViewDeprecated: TaskThumbnailViewDeprecated? = null
- private var onSizeChanged: ((width: Int, height: Int) -> Unit)? = null
- private val outlinePath = Path()
-
- /**
- * Sets the outline bounds of the view. Default to use view's bound as outline when set to null.
- */
- var outlineBounds: Rect? = null
- set(value) {
- field = value
- invalidateOutline()
- }
-
- private val bounds = Rect()
-
- var cornerRadius: Float = 0f
- set(value) {
- field = value
- invalidateOutline()
- }
-
- override fun onFinishInflate() {
- super.onFinishInflate()
- createTaskThumbnailView()
- }
-
- override fun setScaleX(scaleX: Float) {
- super.setScaleX(scaleX)
- taskThumbnailView?.parentScaleXUpdated(scaleX)
- }
-
- override fun setScaleY(scaleY: Float) {
- super.setScaleY(scaleY)
- taskThumbnailView?.parentScaleYUpdated(scaleY)
- }
-
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
- clipToOutline = true
- outlineProvider =
- object : ViewOutlineProvider() {
- override fun getOutline(view: View, outline: Outline) {
- val outlineRect = outlineBounds ?: bounds
- outlinePath.apply {
- rewind()
- addRoundRect(
- outlineRect.left.toFloat(),
- outlineRect.top.toFloat(),
- outlineRect.right.toFloat(),
- outlineRect.bottom.toFloat(),
- cornerRadius / scaleX,
- cornerRadius / scaleY,
- Path.Direction.CW,
- )
- }
- outline.setPath(outlinePath)
- }
- }
- }
-
- override fun onRecycle() {
- taskHeaderView?.isInvisible = true
- onSizeChanged = null
- outlineBounds = null
- taskThumbnailView?.onRecycle()
- taskThumbnailViewDeprecated?.onRecycle()
- }
-
- fun doOnSizeChange(action: (width: Int, height: Int) -> Unit) {
- onSizeChanged = action
- }
-
- override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
- super.onSizeChanged(w, h, oldw, oldh)
- onSizeChanged?.invoke(width, height)
- bounds.set(0, 0, w, h)
- invalidateOutline()
- }
-
- private fun createHeaderView(taskHeaderState: TaskHeaderUiState) {
- if (taskHeaderView == null && taskHeaderState is TaskHeaderUiState.ShowHeader) {
- taskHeaderView =
- LayoutInflater.from(context).inflate(R.layout.task_header_view, this, false)
- as TaskHeaderView
- addView(taskHeaderView, 0)
- }
- }
-
- private fun createTaskThumbnailView() {
- if (taskThumbnailView == null) {
- if (enableRefactorTaskThumbnail()) {
- taskThumbnailView =
- LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false)
- as TaskThumbnailView
- addView(taskThumbnailView)
- } else {
- taskThumbnailViewDeprecated =
- LayoutInflater.from(context)
- .inflate(R.layout.task_thumbnail_deprecated, this, false)
- as TaskThumbnailViewDeprecated
- addView(taskThumbnailViewDeprecated)
- }
- }
- }
-
- fun setState(
- taskHeaderState: TaskHeaderUiState,
- taskThumbnailUiState: TaskThumbnailUiState,
- taskId: Int?,
- ) {
- createHeaderView(taskHeaderState)
- taskHeaderView?.setState(taskHeaderState)
- taskThumbnailView?.setState(taskThumbnailUiState, taskId)
- }
-}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt
deleted file mode 100644
index 09fb5409b5..0000000000
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskHeaderUiState.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.task.thumbnail
-
-import android.graphics.drawable.Drawable
-import android.view.View
-
-sealed class TaskHeaderUiState {
- data class ShowHeader(val header: ThumbnailHeader) : TaskHeaderUiState()
-
- data object HideHeader : TaskHeaderUiState()
-
- data class ThumbnailHeader(
- val icon: Drawable,
- val title: String,
- val clickCloseListener: View.OnClickListener,
- )
-}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
index a5c9ac032f..db593d34d3 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
@@ -19,6 +19,7 @@ package com.android.quickstep.task.thumbnail
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.view.Surface
+import android.view.View.OnClickListener
import androidx.annotation.ColorInt
sealed class TaskThumbnailUiState {
@@ -26,14 +27,37 @@ sealed class TaskThumbnailUiState {
data class BackgroundOnly(@ColorInt val backgroundColor: Int) : TaskThumbnailUiState()
- data object LiveTile : TaskThumbnailUiState()
-
data class SnapshotSplash(val snapshot: Snapshot, val splash: Drawable?) :
TaskThumbnailUiState()
- data class Snapshot(
- val bitmap: Bitmap,
- @Surface.Rotation val thumbnailRotation: Int,
- @ColorInt val backgroundColor: Int,
+ sealed class LiveTile : TaskThumbnailUiState() {
+ data class WithHeader(val header: ThumbnailHeader) : LiveTile()
+
+ data object WithoutHeader : LiveTile()
+ }
+
+ sealed class Snapshot {
+ abstract val bitmap: Bitmap
+ abstract val thumbnailRotation: Int
+ abstract val backgroundColor: Int
+
+ data class WithHeader(
+ override val bitmap: Bitmap,
+ @Surface.Rotation override val thumbnailRotation: Int,
+ @ColorInt override val backgroundColor: Int,
+ val header: ThumbnailHeader,
+ ) : Snapshot()
+
+ data class WithoutHeader(
+ override val bitmap: Bitmap,
+ @Surface.Rotation override val thumbnailRotation: Int,
+ @ColorInt override val backgroundColor: Int,
+ ) : Snapshot()
+ }
+
+ data class ThumbnailHeader(
+ val icon: Drawable,
+ val title: String,
+ val clickCloseListener: OnClickListener,
)
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 78a16f1f39..0edbacc710 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -19,24 +19,32 @@ package com.android.quickstep.task.thumbnail
import android.content.Context
import android.graphics.Color
import android.graphics.Matrix
+import android.graphics.Outline
+import android.graphics.Path
+import android.graphics.Rect
import android.graphics.drawable.ShapeDrawable
import android.util.AttributeSet
import android.util.Log
+import android.view.LayoutInflater
import android.view.View
+import android.view.ViewOutlineProvider
import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.core.view.isInvisible
+import com.android.launcher3.Flags.enableDesktopExplodedView
import com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA
import com.android.launcher3.R
import com.android.launcher3.util.MultiPropertyFactory
+import com.android.launcher3.util.ViewPool
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.views.FixedSizeImageView
+import com.android.quickstep.views.TaskThumbnailViewHeader
-class TaskThumbnailView : FrameLayout {
+class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
private val scrimView: View by lazy { findViewById(R.id.task_thumbnail_scrim) }
private val liveTileView: LiveTileView by lazy { findViewById(R.id.task_thumbnail_live_tile) }
private val thumbnailView: FixedSizeImageView by lazy { findViewById(R.id.task_thumbnail) }
@@ -45,9 +53,30 @@ class TaskThumbnailView : FrameLayout {
private val dimAlpha: MultiPropertyFactory<View> by lazy {
MultiPropertyFactory(scrimView, VIEW_ALPHA, ScrimViewAlpha.entries.size, ::maxOf)
}
+ private val outlinePath = Path()
+ private var onSizeChanged: ((width: Int, height: Int) -> Unit)? = null
+
+ private var taskThumbnailViewHeader: TaskThumbnailViewHeader? = null
private var uiState: TaskThumbnailUiState = Uninitialized
+ /**
+ * Sets the outline bounds of the view. Default to use view's bound as outline when set to null.
+ */
+ var outlineBounds: Rect? = null
+ set(value) {
+ field = value
+ invalidateOutline()
+ }
+
+ private val bounds = Rect()
+
+ var cornerRadius: Float = 0f
+ set(value) {
+ field = value
+ invalidateOutline()
+ }
+
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
@@ -58,8 +87,39 @@ class TaskThumbnailView : FrameLayout {
defStyleAttr: Int,
) : super(context, attrs, defStyleAttr)
- fun onRecycle() {
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ maybeCreateHeader()
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ clipToOutline = true
+ outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ val outlineRect = outlineBounds ?: bounds
+ outlinePath.apply {
+ rewind()
+ addRoundRect(
+ outlineRect.left.toFloat(),
+ outlineRect.top.toFloat(),
+ outlineRect.right.toFloat(),
+ outlineRect.bottom.toFloat(),
+ cornerRadius / scaleX,
+ cornerRadius / scaleY,
+ Path.Direction.CW,
+ )
+ }
+ outline.setPath(outlinePath)
+ }
+ }
+ }
+
+ override fun onRecycle() {
uiState = Uninitialized
+ onSizeChanged = null
+ outlineBounds = null
resetViews()
}
@@ -70,7 +130,7 @@ class TaskThumbnailView : FrameLayout {
resetViews()
when (state) {
is Uninitialized -> {}
- is LiveTile -> drawLiveWindow()
+ is LiveTile -> drawLiveWindow(state)
is SnapshotSplash -> drawSnapshotSplash(state)
is BackgroundOnly -> drawBackground(state.backgroundColor)
}
@@ -95,12 +155,25 @@ class TaskThumbnailView : FrameLayout {
splashIcon.alpha = value
}
- fun parentScaleXUpdated(scaleX: Float) {
+ fun doOnSizeChange(action: (width: Int, height: Int) -> Unit) {
+ onSizeChanged = action
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ onSizeChanged?.invoke(width, height)
+ bounds.set(0, 0, w, h)
+ invalidateOutline()
+ }
+
+ override fun setScaleX(scaleX: Float) {
+ super.setScaleX(scaleX)
// Splash icon should ignore scale on TTV
splashIcon.scaleX = 1 / scaleX
}
- fun parentScaleYUpdated(scaleY: Float) {
+ override fun setScaleY(scaleY: Float) {
+ super.setScaleY(scaleY)
// Splash icon should ignore scale on TTV
splashIcon.scaleY = 1 / scaleY
}
@@ -114,14 +187,20 @@ class TaskThumbnailView : FrameLayout {
splashIcon.setImageDrawable(null)
scrimView.alpha = 0f
setBackgroundColor(Color.BLACK)
+ taskThumbnailViewHeader?.isInvisible = true
}
private fun drawBackground(@ColorInt background: Int) {
setBackgroundColor(background)
}
- private fun drawLiveWindow() {
+ private fun drawLiveWindow(liveTile: LiveTile) {
liveTileView.isInvisible = false
+
+ if (liveTile is LiveTile.WithHeader) {
+ taskThumbnailViewHeader?.isInvisible = false
+ taskThumbnailViewHeader?.setHeader(liveTile.header)
+ }
}
private fun drawSnapshotSplash(snapshotSplash: SnapshotSplash) {
@@ -133,6 +212,11 @@ class TaskThumbnailView : FrameLayout {
}
private fun drawSnapshot(snapshot: Snapshot) {
+ if (snapshot is Snapshot.WithHeader) {
+ taskThumbnailViewHeader?.isInvisible = false
+ taskThumbnailViewHeader?.setHeader(snapshot.header)
+ }
+
drawBackground(snapshot.backgroundColor)
thumbnailView.setImageBitmap(snapshot.bitmap)
thumbnailView.isInvisible = false
@@ -148,6 +232,16 @@ class TaskThumbnailView : FrameLayout {
Log.d(TAG, "[TaskThumbnailView@${Integer.toHexString(hashCode())}] $message")
}
+ private fun maybeCreateHeader() {
+ if (enableDesktopExplodedView() && taskThumbnailViewHeader == null) {
+ taskThumbnailViewHeader =
+ LayoutInflater.from(context)
+ .inflate(R.layout.task_thumbnail_view_header, this, false)
+ as TaskThumbnailViewHeader
+ addView(taskThumbnailViewHeader)
+ }
+ }
+
private companion object {
const val TAG = "TaskThumbnailView"
private const val MAX_SCRIM_ALPHA = 0.4f
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index 8385485253..24c01ae26d 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -32,7 +32,6 @@ import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_NON
import static com.android.wm.shell.shared.split.SplitScreenConstants.getIndex;
import static com.android.wm.shell.shared.split.SplitScreenConstants.isPersistentSnapPosition;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.util.Log;
@@ -93,10 +92,10 @@ public class AppPairsController {
private static final int BITMASK_SIZE = 16;
private static final int BITMASK_FOR_SNAP_POSITION = (1 << BITMASK_SIZE) - 1;
- private Context mContext;
+ private ActivityContext mContext;
private final SplitSelectStateController mSplitSelectStateController;
private final StatsLogManager mStatsLogManager;
- public AppPairsController(Context context,
+ public AppPairsController(ActivityContext context,
SplitSelectStateController splitSelectStateController,
StatsLogManager statsLogManager) {
mContext = context;
@@ -208,7 +207,7 @@ public class AppPairsController {
}
AppPairInfo newAppPair = new AppPairInfo(apps);
- IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
+ IconCache iconCache = LauncherAppState.getInstance(mContext.asContext()).getIconCache();
MODEL_EXECUTOR.execute(() -> {
newAppPair.getAppContents().forEach(member -> {
member.title = "";
@@ -216,8 +215,8 @@ public class AppPairsController {
iconCache.getTitleAndIcon(member, member.getMatchingLookupFlag());
});
MAIN_EXECUTOR.execute(() -> {
- LauncherAccessibilityDelegate delegate =
- QuickstepLauncher.getLauncher(mContext).getAccessibilityDelegate();
+ LauncherAccessibilityDelegate delegate = QuickstepLauncher.getLauncher(
+ mContext.asContext()).getAccessibilityDelegate();
if (delegate != null) {
delegate.addToWorkspace(newAppPair, true, (success) -> {
if (success) {
@@ -300,7 +299,7 @@ public class AppPairsController {
*/
@Nullable
private AppInfo resolveAppInfoByComponent(@NonNull ComponentKey key) {
- AllAppsStore appsStore = ActivityContext.lookupContext(mContext)
+ AllAppsStore appsStore = ActivityContext.lookupContext(mContext.asContext())
.getAppsView().getAppsStore();
// First look up the app info in order of:
@@ -326,7 +325,7 @@ public class AppPairsController {
if (appInfo == null) {
return null;
}
- return appInfo.makeWorkspaceItem(mContext);
+ return appInfo.makeWorkspaceItem(mContext.asContext());
}
/**
@@ -418,7 +417,7 @@ public class AppPairsController {
} else {
// Tapped an app pair while in a single app
final TopTaskTracker.CachedTaskInfo runningTask = topTaskTracker
- .getCachedTopTask(false /* filterOnlyVisibleRecents */);
+ .getCachedTopTask(false /* filterOnlyVisibleRecents */, context.getDisplayId());
mSplitSelectStateController.findLastActiveTasksAndRunCallback(
componentKeys,
@@ -548,6 +547,6 @@ public class AppPairsController {
*/
@VisibleForTesting
public TopTaskTracker getTopTaskTracker() {
- return TopTaskTracker.INSTANCE.get(mContext);
+ return TopTaskTracker.INSTANCE.get(mContext.asContext());
}
}
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index f956a7cfbb..89f1e33d6e 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import android.app.WallpaperManager;
@@ -24,6 +25,8 @@ import android.util.Log;
import android.view.AttachedSurfaceControl;
import android.view.SurfaceControl;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -75,11 +78,14 @@ public class BaseDepthController {
/**
* Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
+ *
* @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
*/
private float mDepth;
- protected SurfaceControl mSurface;
+ protected SurfaceControl mBaseSurface;
+
+ protected SurfaceControl mBaseSurfaceOverride;
// Hints that there is potentially content behind Launcher and that we shouldn't optimize by
// marking the launcher surface as opaque. Only used in certain Launcher states.
@@ -100,6 +106,8 @@ public class BaseDepthController {
protected boolean mWaitingOnSurfaceValidity;
+ private SurfaceControl mBlurSurface = null;
+
public BaseDepthController(Launcher activity) {
mLauncher = activity;
if (Flags.allAppsBlur()) {
@@ -114,6 +122,13 @@ public class BaseDepthController {
new MultiPropertyFactory<>(this, DEPTH, DEPTH_INDEX_COUNT, Float::max);
stateDepth = depthProperty.get(DEPTH_INDEX_STATE_TRANSITION);
widgetDepth = depthProperty.get(DEPTH_INDEX_WIDGET);
+ if (enableOverviewBackgroundWallpaperBlur()) {
+ mBlurSurface = new SurfaceControl.Builder()
+ .setName("Overview Blur")
+ .setHidden(false)
+ .build();
+ }
+
}
protected void setCrossWindowBlursEnabled(boolean isEnabled) {
@@ -151,11 +166,11 @@ public class BaseDepthController {
if (!BlurUtils.supportsBlursOnWindows()) {
return;
}
- if (mSurface == null) {
+ if (mBaseSurface == null) {
Log.d(TAG, "mSurface is null and mCurrentBlur is: " + mCurrentBlur);
return;
}
- if (!mSurface.isValid()) {
+ if (!mBaseSurface.isValid()) {
Log.d(TAG, "mSurface is not valid");
mWaitingOnSurfaceValidity = true;
onInvalidSurface();
@@ -174,10 +189,21 @@ public class BaseDepthController {
mCurrentBlur = !mCrossWindowBlursEnabled || hasOpaqueBg || mPauseBlurs
? 0 : (int) (blurAmount * mMaxBlurRadius);
- SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
- .setBackgroundBlurRadius(mSurface, mCurrentBlur)
- .setOpaque(mSurface, isSurfaceOpaque);
-
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ if (enableOverviewBackgroundWallpaperBlur() && mBlurSurface != null) {
+ // Reparent to launcher for full screen blur.
+ transaction.setBackgroundBlurRadius(mBlurSurface, mCurrentBlur)
+ .reparent(mBlurSurface, mBaseSurface);
+ // Set mBlurSurface to be 1 layer behind mBaseSurface or mBaseSurfaceOverride.
+ if (mBaseSurfaceOverride != null && mBaseSurfaceOverride.isValid()) {
+ transaction.setRelativeLayer(mBlurSurface, mBaseSurfaceOverride, -1);
+ } else {
+ transaction.setRelativeLayer(mBlurSurface, mBaseSurface, -1);
+ }
+ } else {
+ transaction.setBackgroundBlurRadius(mBaseSurface, mCurrentBlur);
+ }
+ transaction.setOpaque(mBaseSurface, isSurfaceOpaque);
// Set early wake-up flags when we know we're executing an expensive operation, this way
// SurfaceFlinger will adjust its internal offsets to avoid jank.
boolean wantsEarlyWakeUp = depth > 0 && depth < 1;
@@ -209,13 +235,26 @@ public class BaseDepthController {
}
/**
+ * Sets the lowest surface that should not be blurred.
+ * <p>
+ * Blur is applied to below {@link #mBaseSurfaceOverride}. When set to {@code null}, blur is
+ * applied
+ * to below {@link #mBaseSurface}.
+ * </p>
+ */
+ public void setBaseSurfaceOverride(@Nullable SurfaceControl baseSurfaceOverride) {
+ this.mBaseSurfaceOverride = baseSurfaceOverride;
+ applyDepthAndBlur();
+ }
+
+ /**
* Sets the specified app target surface to apply the blur to.
*/
- protected void setSurface(SurfaceControl surface) {
- if (mSurface != surface || mWaitingOnSurfaceValidity) {
- mSurface = surface;
+ protected void setBaseSurface(SurfaceControl baseSurface) {
+ if (mBaseSurface != baseSurface || mWaitingOnSurfaceValidity) {
+ mBaseSurface = baseSurface;
Log.d(TAG, "setSurface:\n\tmWaitingOnSurfaceValidity: " + mWaitingOnSurfaceValidity
- + "\n\tmSurface: " + mSurface);
+ + "\n\tmBaseSurface: " + mBaseSurface);
applyDepthAndBlur();
}
}
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
index 3bc9adcba6..e574cc7cdd 100644
--- a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt
@@ -87,7 +87,8 @@ internal constructor(
if (success) {
val runningPackage =
TopTaskTracker.INSTANCE[context].getCachedTopTask(
- /* filterOnlyVisibleRecents */ true
+ /* filterOnlyVisibleRecents */ true,
+ DEFAULT_DISPLAY,
)
.getPackageName()
statsLogManager
diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java b/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java
index a8d3c6d5a3..136c496dce 100644
--- a/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java
+++ b/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java
@@ -18,6 +18,7 @@ package com.android.quickstep.util;
import static android.app.contextualsearch.ContextualSearchManager.ACTION_LAUNCH_CONTEXTUAL_SEARCH;
import static android.app.contextualsearch.ContextualSearchManager.ENTRYPOINT_SYSTEM_ACTION;
import static android.app.contextualsearch.ContextualSearchManager.FEATURE_CONTEXTUAL_SEARCH;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_SYSTEM_ACTION;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -244,7 +245,8 @@ public class ContextualSearchStateManager {
ENTRYPOINT_SYSTEM_ACTION);
if (contextualSearchInvoked) {
String runningPackage = mTopTaskTracker.getCachedTopTask(
- /* filterOnlyVisibleRecents */ true).getPackageName();
+ /* filterOnlyVisibleRecents */ true,
+ DEFAULT_DISPLAY).getPackageName();
StatsLogManager.newInstance(mContext).logger()
.withPackageName(runningPackage)
.log(LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_SYSTEM_ACTION);
diff --git a/quickstep/src/com/android/quickstep/util/DesksUtils.kt b/quickstep/src/com/android/quickstep/util/DesksUtils.kt
index ecddc3fa63..521ba27a8e 100644
--- a/quickstep/src/com/android/quickstep/util/DesksUtils.kt
+++ b/quickstep/src/com/android/quickstep/util/DesksUtils.kt
@@ -38,8 +38,12 @@ class DesksUtils {
task.key.component?.let(::isDesktopWallpaperComponent) == true
@JvmStatic
- fun isDesktopWallpaperTask(taskInfo: TaskInfo) =
- taskInfo.baseIntent.component?.let(::isDesktopWallpaperComponent) == true
+ fun isDesktopWallpaperTask(taskInfo: TaskInfo): Boolean {
+ // TODO: b/403118101 - In some launcher tests, there is a task with baseIntent set to
+ // null. Remove this check after finding out how that task is created.
+ if (taskInfo.baseIntent == null) return false
+ return taskInfo.baseIntent.component?.let(::isDesktopWallpaperComponent) == true
+ }
@JvmStatic
fun isDesktopWallpaperComponent(component: ComponentName) =
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.kt b/quickstep/src/com/android/quickstep/util/DesktopTask.kt
index fbe3bc64d0..7c2729346c 100644
--- a/quickstep/src/com/android/quickstep/util/DesktopTask.kt
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.kt
@@ -17,22 +17,27 @@ package com.android.quickstep.util
import com.android.quickstep.views.TaskViewType
import com.android.systemui.shared.recents.model.Task
+import java.util.Objects
/**
* A [Task] container that can contain N number of tasks that are part of the desktop in recent
- * tasks list. Note that desktops can be empty with no tasks in them. The [deskId] makes sense only
- * when the multiple desks feature is enabled.
+ * tasks list. Note that desktops can be empty with no tasks in them. The [deskId], [displayId]
+ * makes sense only when the multiple desks feature is enabled.
*/
-class DesktopTask(val deskId: Int, tasks: List<Task>) : GroupTask(tasks, TaskViewType.DESKTOP) {
+class DesktopTask(val deskId: Int, val displayId: Int, tasks: List<Task>) :
+ GroupTask(tasks, TaskViewType.DESKTOP) {
- override fun copy() = DesktopTask(deskId, tasks)
+ override fun copy() = DesktopTask(deskId, displayId, tasks)
- override fun toString() = "type=$taskViewType deskId=$deskId tasks=$tasks"
+ override fun toString() = "type=$taskViewType deskId=$deskId displayId=$displayId tasks=$tasks"
override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o !is DesktopTask) return false
if (deskId != o.deskId) return false
+ if (displayId != o.displayId) return false
return super.equals(o)
}
+
+ override fun hashCode() = Objects.hash(super.hashCode(), deskId, displayId)
}
diff --git a/quickstep/src/com/android/quickstep/util/ExternalDisplays.kt b/quickstep/src/com/android/quickstep/util/ExternalDisplays.kt
index 455b3121ec..0aaca31f8b 100644
--- a/quickstep/src/com/android/quickstep/util/ExternalDisplays.kt
+++ b/quickstep/src/com/android/quickstep/util/ExternalDisplays.kt
@@ -16,6 +16,7 @@
package com.android.quickstep.util
+import android.app.TaskInfo
import android.view.Display.DEFAULT_DISPLAY
import android.view.Display.INVALID_DISPLAY
import com.android.systemui.shared.recents.model.Task
@@ -24,10 +25,9 @@ import com.android.systemui.shared.recents.model.Task
val Int.isExternalDisplay
get() = this != DEFAULT_DISPLAY
-/** Returns displayId of this [Task], default to [DEFAULT_DISPLAY] */
-val Task?.displayId
+val Int?.safeDisplayId
get() =
- this?.key?.displayId.let { displayId ->
+ this.let { displayId ->
when (displayId) {
null -> DEFAULT_DISPLAY
INVALID_DISPLAY -> DEFAULT_DISPLAY
@@ -35,6 +35,14 @@ val Task?.displayId
}
}
+/** Returns displayId of this [Task], default to [DEFAULT_DISPLAY] */
+val Task?.safeDisplayId
+ get() = this?.key?.displayId.safeDisplayId
+
/** Returns if this task belongs tto [DEFAULT_DISPLAY] */
val Task?.isExternalDisplay
- get() = displayId.isExternalDisplay
+ get() = safeDisplayId.isExternalDisplay
+
+/** Returns displayId of this [TaskInfo], default to [DEFAULT_DISPLAY] */
+val TaskInfo?.safeDisplayId
+ get() = this?.displayId.safeDisplayId
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.kt b/quickstep/src/com/android/quickstep/util/GroupTask.kt
index add8821b06..2b754e2e0d 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.kt
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.kt
@@ -86,6 +86,8 @@ class SingleTask(task: Task) : GroupTask(listOf(task), TaskViewType.SINGLE) {
return TaskItemInfo(task.task.key.id, wii)
}
}
+
+ override fun hashCode() = super.hashCode()
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java b/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java
index 843619de78..9aded898dc 100644
--- a/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java
+++ b/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java
@@ -35,8 +35,8 @@ public class InputProxyHandlerFactory implements Supplier<InputConsumer> {
private final GestureState mGestureState;
@UiThread
- public InputProxyHandlerFactory(BaseContainerInterface activityInterface,
- GestureState gestureState) {
+ public InputProxyHandlerFactory(
+ BaseContainerInterface activityInterface, GestureState gestureState) {
mContainerInterface = activityInterface;
mGestureState = gestureState;
}
@@ -47,7 +47,8 @@ public class InputProxyHandlerFactory implements Supplier<InputConsumer> {
@Override
public InputConsumer get() {
RecentsViewContainer container = mContainerInterface.getCreatedContainer();
- return container == null ? InputConsumer.NO_OP
+ return container == null
+ ? InputConsumer.createNoOpInputConsumer(mGestureState.getDisplayId())
: new OverviewInputConsumer(mGestureState, container, null, true);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index d6e553d1b0..96a5733a46 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -192,7 +192,7 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
taskViewHeight: Int,
isPrimaryTaskSplitting: Boolean,
) {
- val taskContentView = taskContainer.taskContentView
+ val snapshot = taskContainer.snapshotView
val iconView: View = taskContainer.iconView.asView()
if (enableRefactorTaskThumbnail()) {
builder.add(
@@ -241,11 +241,7 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
val centerThumbnailTranslationX: Float = (taskViewWidth - snapshotViewSize.x) / 2f
val finalScaleX: Float = taskViewWidth.toFloat() / snapshotViewSize.x
builder.add(
- ObjectAnimator.ofFloat(
- taskContentView,
- View.TRANSLATION_X,
- centerThumbnailTranslationX,
- )
+ ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_X, centerThumbnailTranslationX)
)
if (!enableOverviewIconMenu()) {
// icons are anchored from Gravity.END, so need to use negative translation
@@ -254,17 +250,15 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
ObjectAnimator.ofFloat(iconView, View.TRANSLATION_X, -centerIconTranslationX)
)
}
- builder.add(ObjectAnimator.ofFloat(taskContentView, View.SCALE_X, finalScaleX))
+ builder.add(ObjectAnimator.ofFloat(snapshot, View.SCALE_X, finalScaleX))
// Reset other dimensions
// TODO(b/271468547), can't set Y translate to 0, need to account for top space
- taskContentView.scaleY = 1f
+ snapshot.scaleY = 1f
val translateYResetVal: Float =
if (!isPrimaryTaskSplitting) 0f
else deviceProfile.overviewTaskThumbnailTopMarginPx.toFloat()
- builder.add(
- ObjectAnimator.ofFloat(taskContentView, View.TRANSLATION_Y, translateYResetVal)
- )
+ builder.add(ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_Y, translateYResetVal))
} else {
val thumbnailSize = taskViewHeight - deviceProfile.overviewTaskThumbnailTopMarginPx
// Center view first so scaling happens uniformly, alternatively we can move pivotY to 0
@@ -287,22 +281,18 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
}
val finalScaleY: Float = thumbnailSize.toFloat() / snapshotViewSize.y
builder.add(
- ObjectAnimator.ofFloat(
- taskContentView,
- View.TRANSLATION_Y,
- centerThumbnailTranslationY,
- )
+ ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_Y, centerThumbnailTranslationY)
)
if (!enableOverviewIconMenu()) {
// icons are anchored from Gravity.END, so need to use negative translation
builder.add(ObjectAnimator.ofFloat(iconView, View.TRANSLATION_X, 0f))
}
- builder.add(ObjectAnimator.ofFloat(taskContentView, View.SCALE_Y, finalScaleY))
+ builder.add(ObjectAnimator.ofFloat(snapshot, View.SCALE_Y, finalScaleY))
// Reset other dimensions
- taskContentView.scaleX = 1f
- builder.add(ObjectAnimator.ofFloat(taskContentView, View.TRANSLATION_X, 0f))
+ snapshot.scaleX = 1f
+ builder.add(ObjectAnimator.ofFloat(snapshot, View.TRANSLATION_X, 0f))
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index fd8b356708..08f2552157 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -195,7 +195,7 @@ public class SplitSelectStateController {
mRecentTasksModel = recentsModel;
mActivityBackCallback = activityBackCallback;
mSplitAnimationController = new SplitAnimationController(this);
- mAppPairsController = new AppPairsController(mContainer.asContext(), this, statsLogManager);
+ mAppPairsController = new AppPairsController(mContainer, this, statsLogManager);
mSplitSelectDataHolder = new SplitSelectDataHolder(mContainer.asContext());
}
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index d2f9652d69..54ac1cfa3a 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -137,7 +137,8 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim {
mDestinationBoundsTransformed.set(destinationBoundsTransformed);
mSurfaceTransactionHelper = new PipSurfaceTransactionHelper(cornerRadius, shadowRadius);
- final float aspectRatio = destinationBounds.width() / (float) destinationBounds.height();
+ final Rational aspectRatio = new Rational(
+ destinationBounds.width(), destinationBounds.height());
String reasonForCreateOverlay = null; // For debugging purpose.
// Slightly larger app bounds to allow for off by 1 pixel source-rect-hint errors.
@@ -158,16 +159,22 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim {
// not a valid rectangle to use for cropping app surface
reasonForCreateOverlay = "Source rect hint exceeds display bounds " + sourceRectHint;
sourceRectHint.setEmpty();
- } else if (!PictureInPictureParams.isSameAspectRatio(sourceRectHint,
- new Rational(destinationBounds.width(), destinationBounds.height()))) {
- // The source rect hint does not aspect ratio
- reasonForCreateOverlay = "Source rect hint does not match aspect ratio "
- + sourceRectHint + " aspect ratio " + aspectRatio;
- sourceRectHint.setEmpty();
+ } else {
+ final Rational srcAspectRatio = new Rational(
+ sourceRectHint.width(), sourceRectHint.height());
+ if (!PictureInPictureParams.isSameAspectRatio(destinationBounds, srcAspectRatio)) {
+ // The aspect ratio of destination bounds does not match source rect hint.
+ // We use the aspect ratio of source rect hint to check against destination bounds
+ // here to avoid upscaling error.
+ reasonForCreateOverlay = "Source rect hint:" + sourceRectHint
+ + " does not match destination bounds:" + destinationBounds;
+ sourceRectHint.setEmpty();
+ }
}
if (sourceRectHint.isEmpty()) {
- mSourceRectHint.set(getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio));
+ mSourceRectHint.set(
+ getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio.floatValue()));
mPipContentOverlay = new PipContentOverlay.PipAppIconOverlay(view.getContext(),
mAppBounds, mDestinationBounds,
new IconProvider(context).getIcon(mActivityInfo), appIconSizePx);
diff --git a/quickstep/src/com/android/quickstep/views/BlurUtils.kt b/quickstep/src/com/android/quickstep/views/BlurUtils.kt
new file mode 100644
index 0000000000..d6b2a055b7
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/BlurUtils.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views
+
+import com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle
+
+/** Applies blur either behind launcher surface or live tile app. */
+class BlurUtils(private val recentsView: RecentsView<*, *>) {
+
+ fun setDrawLiveTileBelowRecents(drawBelowRecents: Boolean) {
+ val liveTileRemoteTargetHandles =
+ if (
+ recentsView.remoteTargetHandles != null &&
+ recentsView.recentsAnimationController != null
+ )
+ recentsView.remoteTargetHandles
+ else null
+ setDrawBelowRecents(drawBelowRecents, liveTileRemoteTargetHandles)
+ }
+
+ /**
+ * Set surface in [remoteTargetHandles] to be above or below Recents layer, and update the base
+ * layer to apply blur to in BaseDepthController.
+ */
+ fun setDrawBelowRecents(
+ drawBelowRecents: Boolean,
+ remoteTargetHandles: Array<RemoteTargetHandle>? = null,
+ ) {
+ remoteTargetHandles?.forEach { it.taskViewSimulator.setDrawsBelowRecents(drawBelowRecents) }
+ if (enableOverviewBackgroundWallpaperBlur()) {
+ recentsView.depthController?.setBaseSurfaceOverride(
+ // Blurs behind launcher layer.
+ if (!drawBelowRecents || remoteTargetHandles == null) {
+ null
+ } else {
+ // Blurs behind live tile. blur will be applied behind window
+ // which farthest from user in case of desktop and split apps.
+ remoteTargetHandles
+ .maxByOrNull { it.transformParams.targetSet.firstAppTarget.leash.layerId }
+ ?.transformParams
+ ?.targetSet
+ ?.firstAppTarget
+ ?.leash
+ }
+ )
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 27657b4c0f..8b124555f1 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -25,6 +25,7 @@ import android.graphics.RectF
import android.util.AttributeSet
import android.util.Log
import android.util.Size
+import android.view.Display.INVALID_DISPLAY
import android.view.Gravity
import android.view.View
import android.view.ViewStub
@@ -55,10 +56,10 @@ import com.android.quickstep.recents.di.get
import com.android.quickstep.recents.domain.model.DesktopTaskBoundsData
import com.android.quickstep.recents.ui.viewmodel.DesktopTaskViewModel
import com.android.quickstep.recents.ui.viewmodel.TaskData
-import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.RecentsOrientedState
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.enableMultipleDesktops
import kotlin.math.roundToInt
/** TaskView that contains all tasks that are part of the desktop. */
@@ -69,18 +70,34 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
type = TaskViewType.DESKTOP,
thumbnailFullscreenParams = DesktopFullscreenDrawParams(context),
) {
- var deskId = DesktopVisibilityController.INACTIVE_DESK_ID
+ val deskId
+ get() = desktopTask?.deskId ?: DesktopVisibilityController.INACTIVE_DESK_ID
+
+ private var desktopTask: DesktopTask? = null
private val contentViewFullscreenParams = FullscreenDrawParams(context)
- private val taskContentViewPool =
- ViewPool<TaskContentView>(
- context,
- this,
- R.layout.task_content_view,
- VIEW_POOL_MAX_SIZE,
- VIEW_POOL_INITIAL_SIZE,
- )
+ private val taskThumbnailViewDeprecatedPool =
+ if (!enableRefactorTaskThumbnail()) {
+ ViewPool<TaskThumbnailViewDeprecated>(
+ context,
+ this,
+ R.layout.task_thumbnail_deprecated,
+ VIEW_POOL_MAX_SIZE,
+ VIEW_POOL_INITIAL_SIZE,
+ )
+ } else null
+
+ private val taskThumbnailViewPool =
+ if (enableRefactorTaskThumbnail()) {
+ ViewPool<TaskThumbnailView>(
+ context,
+ this,
+ R.layout.task_thumbnail,
+ VIEW_POOL_MAX_SIZE,
+ VIEW_POOL_INITIAL_SIZE,
+ )
+ } else null
private val tempPointF = PointF()
private val lastComputedTaskSize = Rect()
@@ -112,6 +129,14 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
positionTaskWindows()
}
+ override val displayId: Int
+ get() =
+ if (enableMultipleDesktops(context)) {
+ desktopTask?.displayId ?: INVALID_DISPLAY
+ } else {
+ super.displayId
+ }
+
private fun getRemoteTargetHandle(taskId: Int): RemoteTargetHandle? =
remoteTargetHandles?.firstOrNull {
it.transformParams.targetSet.firstAppTargetTaskId == taskId
@@ -230,7 +255,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
// for all cases where the progress is non-zero.
if (explodeProgress == 0.0f || explodeProgress == 1.0f) {
// Reset scaling and translation that may have been applied during animation.
- it.taskContentView.apply {
+ it.snapshotView.apply {
scaleX = 1.0f
scaleY = 1.0f
translationX = 0.0f
@@ -238,7 +263,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
}
// Position the task to the same position as it would be on the desktop
- it.taskContentView?.updateLayoutParams<LayoutParams> {
+ it.snapshotView.updateLayoutParams<LayoutParams> {
gravity = Gravity.LEFT or Gravity.TOP
width = taskWidth.toInt()
height = taskHeight.toInt()
@@ -249,7 +274,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
if (
enableDesktopRecentsTransitionsCornersBugfix() && enableRefactorTaskThumbnail()
) {
- it.taskContentView?.outlineBounds =
+ it.thumbnailView.outlineBounds =
if (intersects(overviewTaskPosition, screenRect))
Rect(overviewTaskPosition).apply {
intersectUnchecked(screenRect)
@@ -266,7 +291,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
} else {
// During the animation, apply translation and scale such that the view is
// transformed to where we want, without triggering layout.
- it.taskContentView.apply {
+ it.snapshotView.apply {
pivotX = 0.0f
pivotY = 0.0f
translationX = taskLeft - left
@@ -284,7 +309,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
orientedState: RecentsOrientedState,
taskOverlayFactory: TaskOverlayFactory,
) {
- deskId = desktopTask.deskId
+ this.desktopTask = desktopTask
// TODO(b/370495260): Minimized tasks should not be filtered with desktop exploded view
// support.
// Minimized tasks should not be shown in Overview.
@@ -300,19 +325,17 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
val backgroundViewIndex = contentView.indexOfChild(backgroundView)
taskContainers =
tasks.map { task ->
- val taskContentView = taskContentViewPool.view
- contentView.addView(taskContentView, backgroundViewIndex + 1)
val snapshotView =
if (enableRefactorTaskThumbnail()) {
- taskContentView.findViewById<TaskThumbnailView>(R.id.snapshot)
+ taskThumbnailViewPool!!.view
} else {
- taskContentView.findViewById<TaskThumbnailViewDeprecated>(R.id.snapshot)
+ taskThumbnailViewDeprecatedPool!!.view
}
+ contentView.addView(snapshotView, backgroundViewIndex + 1)
TaskContainer(
this,
task,
- taskContentView,
snapshotView,
iconView,
TransformingTouchDelegate(iconView.asView()),
@@ -336,7 +359,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
override fun onRecycle() {
super.onRecycle()
- deskId = DesktopVisibilityController.INACTIVE_DESK_ID
+ desktopTask = null
explodeProgress = 0.0f
viewModel = null
visibility = VISIBLE
@@ -465,8 +488,12 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
}
private fun removeAndRecycleThumbnailView(taskContainer: TaskContainer) {
- contentView.removeView(taskContainer.taskContentView)
- taskContentViewPool.recycle(taskContainer.taskContentView)
+ contentView.removeView(taskContainer.snapshotView)
+ if (enableRefactorTaskThumbnail()) {
+ taskThumbnailViewPool!!.recycle(taskContainer.thumbnailView)
+ } else {
+ taskThumbnailViewDeprecatedPool!!.recycle(taskContainer.thumbnailViewDeprecated)
+ }
}
private fun updateTaskPositions() {
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index 71a4dde395..faa9e2893b 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -24,6 +24,7 @@ import android.view.View
import android.view.ViewStub
import com.android.internal.jank.Cuj
import com.android.launcher3.Flags.enableOverviewIconMenu
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.util.RunnableList
@@ -77,8 +78,8 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
val splitBoundsConfig = splitBoundsConfig ?: return
val inSplitSelection = getThisTaskCurrentlyInSplitSelection() != INVALID_TASK_ID
pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
- leftTopTaskContainer.taskContentView,
- rightBottomTaskContainer.taskContentView,
+ leftTopTaskContainer.snapshotView,
+ rightBottomTaskContainer.snapshotView,
widthSize,
heightSize,
splitBoundsConfig,
@@ -94,8 +95,12 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
override fun inflateViewStubs() {
super.inflateViewStubs()
- findViewById<ViewStub>(R.id.bottomright_task_content_view)
- ?.apply { layoutResource = R.layout.task_content_view }
+ findViewById<ViewStub>(R.id.bottomright_snapshot)
+ ?.apply {
+ layoutResource =
+ if (enableRefactorTaskThumbnail()) R.layout.task_thumbnail
+ else R.layout.task_thumbnail_deprecated
+ }
?.inflate()
findViewById<ViewStub>(R.id.bottomRight_icon)
?.apply {
@@ -123,7 +128,6 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
listOf(
createTaskContainer(
primaryTask,
- R.id.task_content_view,
R.id.snapshot,
R.id.icon,
R.id.show_windows,
@@ -133,8 +137,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
),
createTaskContainer(
secondaryTask,
- R.id.bottomright_task_content_view,
- R.id.snapshot,
+ R.id.bottomright_snapshot,
R.id.bottomRight_icon,
R.id.show_windows_right,
R.id.bottomRight_digital_wellbeing_toast,
@@ -182,7 +185,6 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
val splitBoundsConfig = splitBoundsConfig ?: return
val deviceProfile = container.deviceProfile
val taskIconHeight = deviceProfile.overviewTaskIconSizePx
- val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
val inSplitSelection = getThisTaskCurrentlyInSplitSelection() != INVALID_TASK_ID
var oneIconHiddenDueToSmallWidth = false
@@ -211,6 +213,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
}
if (enableOverviewIconMenu()) {
+ val isDeviceRtl = Utilities.isRtl(resources)
val groupedTaskViewSizes =
pagedOrientationHandler.getGroupedTaskViewSizes(
deviceProfile,
@@ -226,7 +229,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
groupedTaskViewSizes.first.y,
layoutParams.height,
layoutParams.width,
- isRtl,
+ isDeviceRtl,
deviceProfile,
splitBoundsConfig,
inSplitSelection,
@@ -237,11 +240,11 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
leftTopTaskContainer.iconView.asView(),
rightBottomTaskContainer.iconView.asView(),
taskIconHeight,
- leftTopTaskContainer.taskContentView.measuredWidth,
- leftTopTaskContainer.taskContentView.measuredHeight,
+ leftTopTaskContainer.snapshotView.measuredWidth,
+ leftTopTaskContainer.snapshotView.measuredHeight,
measuredHeight,
measuredWidth,
- isRtl,
+ isLayoutRtl,
deviceProfile,
splitBoundsConfig,
inSplitSelection,
diff --git a/quickstep/src/com/android/quickstep/views/IconAppChipView.kt b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
index 7683a1500a..f4fd12792a 100644
--- a/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
+++ b/quickstep/src/com/android/quickstep/views/IconAppChipView.kt
@@ -122,7 +122,7 @@ constructor(
field = max(value, minMaxWidth)
}
- var isExpanded: Boolean = false
+ var status: AppChipStatus = AppChipStatus.Collapsed
private set
override fun onFinishInflate() {
@@ -358,8 +358,8 @@ constructor(
ObjectAnimator.ofFloat(iconArrowView, TRANSLATION_X, arrowTranslationWithRtl),
ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, -1f),
)
- animator!!.setDuration(MENU_BACKGROUND_REVEAL_DURATION.toLong())
- isExpanded = true
+ animator!!.duration = MENU_BACKGROUND_REVEAL_DURATION.toLong()
+ status = AppChipStatus.Expanded
} else {
// Clip expanded text with reveal animation so it doesn't go beyond the edge of the menu
val expandedTextClipAnim =
@@ -393,8 +393,8 @@ constructor(
ObjectAnimator.ofFloat(iconArrowView, TRANSLATION_X, 0f),
ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, 1f),
)
- animator!!.setDuration(MENU_BACKGROUND_HIDE_DURATION.toLong())
- isExpanded = false
+ animator!!.duration = MENU_BACKGROUND_HIDE_DURATION.toLong()
+ status = AppChipStatus.Collapsed
}
if (!animated) animator!!.duration = 0
@@ -432,8 +432,18 @@ constructor(
}
}
+ fun reset() {
+ setText(null)
+ setDrawable(null)
+ }
+
override fun asView(): View = this
+ enum class AppChipStatus {
+ Expanded,
+ Collapsed,
+ }
+
private companion object {
private val SUM_AGGREGATOR = FloatBiFunction { a: Float, b: Float -> a + b }
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 0f1c2941b3..b86d0c85de 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -18,7 +18,9 @@ package com.android.quickstep.views;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
+import static com.android.launcher3.LauncherState.ADD_DESK_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
@@ -52,7 +54,7 @@ import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitSelectStateController;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import kotlin.Unit;
@@ -150,7 +152,14 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
public void onStateTransitionStart(LauncherState toState) {
setOverviewStateEnabled(toState.isRecentsViewVisible);
- setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile()));
+ if (enableGridOnlyOverview()) {
+ if (toState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile())) {
+ setOverviewGridEnabled(true);
+ }
+ } else {
+ setOverviewGridEnabled(
+ toState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile()));
+ }
setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
if (toState == OVERVIEW_MODAL_TASK) {
setOverviewSelectEnabled(true);
@@ -169,6 +178,11 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
@Override
public void onStateTransitionComplete(LauncherState finalState) {
DesktopVisibilityController.INSTANCE.get(mContainer).onLauncherStateChanged(finalState);
+ if (enableGridOnlyOverview()) {
+ if (!finalState.displayOverviewTasksAsGrid(mContainer.getDeviceProfile())) {
+ setOverviewGridEnabled(false);
+ }
+ }
if (!finalState.isRecentsViewVisible) {
// Clean-up logic that occurs when recents is no longer in use/visible.
@@ -184,10 +198,8 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
if (finalState.isRecentsViewVisible && finalState != OVERVIEW_MODAL_TASK) {
setTaskBorderEnabled(true);
}
-
if (isOverlayEnabled) {
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
+ mBlurUtils.setDrawLiveTileBelowRecents(true);
}
}
@@ -198,7 +210,10 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
LauncherState state = getStateManager().getState();
boolean hasClearAllButton = (state.getVisibleElements(mContainer)
& CLEAR_ALL_BUTTON) != 0;
+ boolean hasAddDeskButton = (state.getVisibleElements(mContainer)
+ & ADD_DESK_BUTTON) != 0;
setDisallowScrollToClearAll(!hasClearAllButton);
+ setDisallowScrollToAddDesk(!hasAddDeskButton);
}
}
@@ -260,8 +275,8 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
}
@Override
- public void onGestureAnimationStart(Task[] runningTasks) {
- super.onGestureAnimationStart(runningTasks);
+ public void onGestureAnimationStart(GroupedTaskInfo groupedTaskInfo) {
+ super.onGestureAnimationStart(groupedTaskInfo);
if (!ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
// TODO: b/333533253 - Remove after flag rollout
DesktopVisibilityController.INSTANCE.get(mContainer).setRecentsGestureStart();
@@ -276,7 +291,7 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
GestureState.GestureEndTarget endTarget = mCurrentGestureEndTarget;
if (endTarget == GestureState.GestureEndTarget.LAST_TASK
&& desktopVisibilityController.isInDesktopModeAndNotInOverview(
- mContainer.getDisplayId())) {
+ mContainer.getDisplayId())) {
// Recents gesture was cancelled and we are returning to the previous task.
// After super class has handled clean up, show desktop apps on top again
showDesktopApps = true;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
index 4ce18f50a1..6acaeae8db 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
@@ -24,7 +24,6 @@ import androidx.dynamicanimation.animation.SpringForce
import com.android.launcher3.Flags.enableGridOnlyOverview
import com.android.launcher3.R
import com.android.launcher3.Utilities.boundToRange
-import com.android.launcher3.touch.SingleAxisSwipeDetector
import com.android.launcher3.util.DynamicResource
import com.android.launcher3.util.MSDLPlayerWrapper
import com.android.quickstep.util.TaskGridNavHelper
@@ -33,6 +32,7 @@ import com.google.android.msdl.data.model.MSDLToken
import com.google.android.msdl.domain.InteractionProperties
import kotlin.math.abs
import kotlin.math.roundToInt
+import kotlin.math.sign
/**
* Helper class for [RecentsView]. This util class contains refactored and extracted functions from
@@ -51,7 +51,6 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
draggedTaskView: TaskView?,
velocity: Float,
isDismissing: Boolean,
- detector: SingleAxisSwipeDetector,
dismissLength: Int,
onEndRunnable: () -> Unit,
): SpringAnimation? {
@@ -60,11 +59,14 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
FloatPropertyCompat.createFloatPropertyCompat(
draggedTaskView.secondaryDismissTranslationProperty
)
+ val minVelocity =
+ recentsView.pagedOrientationHandler.getSecondaryDimension(draggedTaskView).toFloat()
+ val startVelocity = abs(velocity).coerceAtLeast(minVelocity) * velocity.sign
// Animate dragged task towards dismissal or rest state.
val draggedTaskViewSpringAnimation =
SpringAnimation(draggedTaskView, taskDismissFloatProperty)
.setSpring(createExpressiveDismissSpringForce())
- .setStartVelocity(if (detector.isFling(velocity)) velocity else 0f)
+ .setStartVelocity(startVelocity)
.addUpdateListener { animation, value, _ ->
if (isDismissing && abs(value) >= abs(dismissLength)) {
animation.cancel()
@@ -82,6 +84,7 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
runTaskGridReflowSpringAnimation(
draggedTaskView,
getDismissedTaskGapForReflow(draggedTaskView),
+ onEndRunnable,
)
} else {
recentsView.dismissTaskView(
@@ -89,70 +92,114 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
/* animateTaskView = */ false,
/* removeTask = */ true,
)
+ onEndRunnable()
}
} else {
recentsView.onDismissAnimationEnds()
+ onEndRunnable()
}
- onEndRunnable()
}
if (!isDismissing) {
- addNeighboringSpringAnimationsForDismissCancel(
+ addNeighborSettlingSpringAnimations(
draggedTaskView,
draggedTaskViewSpringAnimation,
+ driverProgressThreshold = 0f,
+ isSpringDirectionVertical = true,
+ minVelocity = startVelocity,
)
}
return draggedTaskViewSpringAnimation
}
- private fun addNeighboringSpringAnimationsForDismissCancel(
+ private fun addNeighborSettlingSpringAnimations(
draggedTaskView: TaskView,
- draggedTaskViewSpringAnimation: SpringAnimation,
+ springAnimationDriver: SpringAnimation,
+ tasksToExclude: List<TaskView> = emptyList(),
+ driverProgressThreshold: Float,
+ isSpringDirectionVertical: Boolean,
+ minVelocity: Float,
) {
// Empty spring animation exists for conditional start, and to drive neighboring springs.
val neighborsToSettle =
SpringAnimation(FloatValueHolder()).setSpring(createExpressiveDismissSpringForce())
- var lastPosition = 0f
- var startSettling = false
- draggedTaskViewSpringAnimation.addUpdateListener { _, value, velocity ->
- // Start the settling animation the first time the dragged task passes the origin (from
- // negative displacement to positive displacement). We do not check for an exact value
- // to compare to, as the update listener does not necessarily hit every value (e.g. a
- // value of zero). Do not check again once it has started settling, as a spring can
- // bounce past the origin multiple times depending on the stiffness and damping ratio.
- if (startSettling) return@addUpdateListener
- if (lastPosition < 0 && value >= 0) {
- startSettling = true
- }
- lastPosition = value
- if (startSettling) {
- neighborsToSettle.setStartVelocity(velocity).animateToFinalPosition(0f)
- playDismissSettlingHaptic(velocity)
- }
- }
// Add tasks before dragged index, fanning out from the dragged task.
// The order they are added matters, as each spring drives the next.
var previousNeighbor = neighborsToSettle
- getTasksOffsetPairAdjacentToDraggedTask(draggedTaskView, towardsStart = true).forEach {
- (taskView, offset) ->
- previousNeighbor =
- createNeighboringTaskViewSpringAnimation(
- taskView,
- offset * ADDITIONAL_DISMISS_DAMPING_RATIO,
- previousNeighbor,
- )
- }
+ getTasksOffsetPairAdjacentToDraggedTask(draggedTaskView, towardsStart = true)
+ .filter { (taskView, _) -> !tasksToExclude.contains(taskView) }
+ .forEach { (taskView, offset) ->
+ previousNeighbor =
+ createNeighboringTaskViewSpringAnimation(
+ taskView,
+ offset * ADDITIONAL_DISMISS_DAMPING_RATIO,
+ previousNeighbor,
+ isSpringDirectionVertical,
+ )
+ }
// Add tasks after dragged index, fanning out from the dragged task.
// The order they are added matters, as each spring drives the next.
previousNeighbor = neighborsToSettle
- getTasksOffsetPairAdjacentToDraggedTask(draggedTaskView, towardsStart = false).forEach {
- (taskView, offset) ->
- previousNeighbor =
- createNeighboringTaskViewSpringAnimation(
- taskView,
- offset * ADDITIONAL_DISMISS_DAMPING_RATIO,
- previousNeighbor,
- )
+ getTasksOffsetPairAdjacentToDraggedTask(draggedTaskView, towardsStart = false)
+ .filter { (taskView, _) -> !tasksToExclude.contains(taskView) }
+ .forEach { (taskView, offset) ->
+ previousNeighbor =
+ createNeighboringTaskViewSpringAnimation(
+ taskView,
+ offset * ADDITIONAL_DISMISS_DAMPING_RATIO,
+ previousNeighbor,
+ isSpringDirectionVertical,
+ )
+ }
+
+ val isCurrentDisplacementAboveOrigin =
+ recentsView.pagedOrientationHandler.isGoingUp(
+ draggedTaskView.secondaryDismissTranslationProperty.get(draggedTaskView),
+ recentsView.isRtl,
+ )
+ addThresholdSpringAnimationTrigger(
+ springAnimationDriver,
+ progressThreshold = driverProgressThreshold,
+ neighborsToSettle,
+ isCurrentDisplacementAboveOrigin,
+ minVelocity,
+ )
+ }
+
+ /** As spring passes threshold for the first time, run conditional spring with velocity. */
+ private fun addThresholdSpringAnimationTrigger(
+ springAnimationDriver: SpringAnimation,
+ progressThreshold: Float,
+ conditionalSpring: SpringAnimation,
+ isCurrentDisplacementAboveOrigin: Boolean,
+ minVelocity: Float,
+ ) {
+ val runSettlingAtVelocity = { velocity: Float ->
+ conditionalSpring.setStartVelocity(velocity).animateToFinalPosition(0f)
+ playDismissSettlingHaptic(velocity)
+ }
+ if (isCurrentDisplacementAboveOrigin) {
+ var lastPosition = 0f
+ var startSettling = false
+ springAnimationDriver.addUpdateListener { _, value, velocity ->
+ // We do not compare to the threshold directly, as the update listener
+ // does not necessarily hit every value. Do not check again once it has started
+ // settling, as a spring can bounce past the end value multiple times.
+ if (startSettling) return@addUpdateListener
+ if (
+ lastPosition < progressThreshold && value >= progressThreshold ||
+ lastPosition > progressThreshold && value <= progressThreshold
+ ) {
+ startSettling = true
+ }
+ lastPosition = value
+ if (startSettling) {
+ runSettlingAtVelocity(velocity)
+ }
+ }
+ } else {
+ // Run settling animations immediately when displacement is already below settled state.
+ runSettlingAtVelocity(minVelocity)
}
}
@@ -208,21 +255,25 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
taskView: TaskView,
dampingOffsetRatio: Float,
previousNeighborSpringAnimation: SpringAnimation,
+ springingDirectionVertical: Boolean,
): SpringAnimation {
+ val springProperty =
+ if (springingDirectionVertical) taskView.secondaryDismissTranslationProperty
+ else taskView.primaryDismissTranslationProperty
val neighboringTaskViewSpringAnimation =
- SpringAnimation(
- taskView,
- FloatPropertyCompat.createFloatPropertyCompat(
- taskView.secondaryDismissTranslationProperty
- ),
- )
+ SpringAnimation(taskView, FloatPropertyCompat.createFloatPropertyCompat(springProperty))
.setSpring(createExpressiveDismissSpringForce(dampingOffsetRatio))
// Update live tile on spring animation.
if (taskView.isRunningTask && recentsView.enableDrawingLiveTile) {
neighboringTaskViewSpringAnimation.addUpdateListener { _, _, _ ->
recentsView.runActionOnRemoteHandles { remoteTargetHandle ->
- remoteTargetHandle.taskViewSimulator.taskSecondaryTranslation.value =
- taskView.secondaryDismissTranslationProperty.get(taskView)
+ val taskTranslation =
+ if (springingDirectionVertical) {
+ remoteTargetHandle.taskViewSimulator.taskSecondaryTranslation
+ } else {
+ remoteTargetHandle.taskViewSimulator.taskPrimaryTranslation
+ }
+ taskTranslation.value = springProperty.get(taskView)
}
recentsView.redrawLiveTile()
}
@@ -268,10 +319,10 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
val maxDismissSettlingVelocity =
recentsView.pagedOrientationHandler.getSecondaryDimension(recentsView)
MSDLPlayerWrapper.INSTANCE.get(recentsView.context)
- .playToken(
+ ?.playToken(
MSDLToken.CANCEL,
InteractionProperties.DynamicVibrationScale(
- boundToRange(velocity / maxDismissSettlingVelocity, 0f, 1f),
+ boundToRange(abs(velocity) / maxDismissSettlingVelocity, 0f, 1f),
VibrationAttributes.Builder()
.setUsage(VibrationAttributes.USAGE_TOUCH)
.setFlags(VibrationAttributes.FLAG_PIPELINED_EFFECT)
@@ -312,6 +363,7 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
private fun runTaskGridReflowSpringAnimation(
dismissedTaskView: TaskView,
dismissedTaskGap: Float,
+ onEndRunnable: () -> Unit,
) {
// Empty spring animation exists for conditional start, and to drive neighboring springs.
val springAnimationDriver =
@@ -319,32 +371,39 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
.setSpring(createExpressiveGridReflowSpringForce(finalPosition = dismissedTaskGap))
val towardsStart = if (recentsView.isRtl) dismissedTaskGap < 0 else dismissedTaskGap > 0
+ var tasksToReflow: List<TaskView>
// Build the chains of Spring Animations
when {
!recentsView.showAsGrid() -> {
- buildDismissReflowSpringAnimationChain(
+ tasksToReflow =
getTasksToReflow(
recentsView.mUtils.taskViews.toList(),
dismissedTaskView,
towardsStart,
- ),
+ )
+ buildDismissReflowSpringAnimationChain(
+ tasksToReflow,
dismissedTaskGap,
previousSpring = springAnimationDriver,
)
}
dismissedTaskView.isLargeTile -> {
+ tasksToReflow =
+ getTasksToReflow(
+ recentsView.mUtils.getLargeTaskViews(),
+ dismissedTaskView,
+ towardsStart,
+ )
val lastSpringAnimation =
buildDismissReflowSpringAnimationChain(
- getTasksToReflow(
- recentsView.mUtils.getLargeTaskViews(),
- dismissedTaskView,
- towardsStart,
- ),
+ tasksToReflow,
dismissedTaskGap,
previousSpring = springAnimationDriver,
)
// Add all top and bottom grid tasks when animating towards the end of the grid.
if (!towardsStart) {
+ tasksToReflow += recentsView.mUtils.getTopRowTaskViews()
+ tasksToReflow += recentsView.mUtils.getBottomRowTaskViews()
buildDismissReflowSpringAnimationChain(
recentsView.mUtils.getTopRowTaskViews(),
dismissedTaskGap,
@@ -358,33 +417,53 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
}
}
recentsView.isOnGridBottomRow(dismissedTaskView) -> {
- buildDismissReflowSpringAnimationChain(
+ tasksToReflow =
getTasksToReflow(
recentsView.mUtils.getBottomRowTaskViews(),
dismissedTaskView,
towardsStart,
- ),
+ )
+ buildDismissReflowSpringAnimationChain(
+ tasksToReflow,
dismissedTaskGap,
previousSpring = springAnimationDriver,
)
}
else -> {
- buildDismissReflowSpringAnimationChain(
+ tasksToReflow =
getTasksToReflow(
recentsView.mUtils.getTopRowTaskViews(),
dismissedTaskView,
towardsStart,
- ),
+ )
+ buildDismissReflowSpringAnimationChain(
+ tasksToReflow,
dismissedTaskGap,
previousSpring = springAnimationDriver,
)
}
}
+ if (tasksToReflow.isNotEmpty()) {
+ addNeighborSettlingSpringAnimations(
+ dismissedTaskView,
+ springAnimationDriver,
+ tasksToExclude = tasksToReflow,
+ driverProgressThreshold = dismissedTaskGap,
+ isSpringDirectionVertical = false,
+ minVelocity = 0f,
+ )
+ } else {
+ springAnimationDriver.addEndListener { _, _, _, _ ->
+ // Play the same haptic as when neighbors spring into place.
+ MSDLPlayerWrapper.INSTANCE.get(recentsView.context)?.playToken(MSDLToken.CANCEL)
+ }
+ }
+
// Start animations and remove the dismissed task at the end, dismiss immediately if no
// neighboring tasks exist.
val runGridEndAnimationAndRelayout = {
- recentsView.expressiveDismissTaskView(dismissedTaskView)
+ recentsView.expressiveDismissTaskView(dismissedTaskView, onEndRunnable)
}
springAnimationDriver?.apply {
addEndListener { _, _, _, _ -> runGridEndAnimationAndRelayout() }
@@ -429,8 +508,8 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
else -> 1f
} * (if (recentsView.isRtl) 1f else -1f)
- return (dismissedTaskView.layoutParams.width + recentsView.pageSpacing) *
- dismissHorizontalFactor
+ return (recentsView.pagedOrientationHandler.getPrimarySize(dismissedTaskView) +
+ recentsView.pageSpacing) * dismissHorizontalFactor
}
private fun getTasksToReflow(
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 6067550507..ea95206811 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -34,14 +34,13 @@ import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
-import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
-import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.Flags.enableAdditionalHomeAnimations;
import static com.android.launcher3.Flags.enableDesktopExplodedView;
import static com.android.launcher3.Flags.enableDesktopTaskAlphaAnimation;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.Flags.enableLargeDesktopWindowingTile;
+import static com.android.launcher3.Flags.enableOverviewBackgroundWallpaperBlur;
import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
import static com.android.launcher3.Flags.enableSeparateExternalDisplayTasks;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
@@ -88,7 +87,6 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
-import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
import android.content.LocusId;
@@ -142,7 +140,6 @@ import androidx.annotation.UiThread;
import androidx.core.graphics.ColorUtils;
import androidx.dynamicanimation.animation.SpringAnimation;
-import com.android.app.tracing.TraceUtilsKt;
import com.android.internal.jank.Cuj;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
@@ -171,7 +168,6 @@ import com.android.launcher3.statemanager.StatefulContainer;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.OverScroll;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.CancellableTask;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.IntArray;
@@ -237,6 +233,7 @@ import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.VibrationConstants;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
@@ -244,6 +241,7 @@ import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.common.pip.IPipAnimationListener;
+import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
@@ -277,7 +275,7 @@ public abstract class RecentsView<
HighResLoadingState.HighResLoadingStateChangedCallback,
TaskVisualsChangeListener, DesktopVisibilityListener {
- private static final String TAG = "RecentsView";
+ protected static final String TAG = "RecentsView";
private static final boolean DEBUG = false;
public static final FloatProperty<RecentsView<?, ?>> CONTENT_ALPHA =
@@ -536,7 +534,6 @@ public abstract class RecentsView<
protected final Rect mLastComputedTaskSize = new Rect();
protected final Rect mLastComputedGridSize = new Rect();
protected final Rect mLastComputedGridTaskSize = new Rect();
- private TaskView mSelectedTask = null;
// How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
@Nullable
protected Float mLastComputedTaskStartPushOutDistance = null;
@@ -571,10 +568,10 @@ public abstract class RecentsView<
private DesktopVisibilityController mDesktopVisibilityController = null;
/**
- * Reflects if Recents is currently in the middle of a gesture, and if so, which tasks are
- * running. If a gesture is not in progress, this will be null.
+ * Reflects if Recents is currently in the middle of a gesture, and if so, which related
+ * [GroupedTaskInfo] is running. If a gesture is not in progress, this will be null.
*/
- private @Nullable Task[] mActiveGestureRunningTasks;
+ private @Nullable GroupedTaskInfo mActiveGestureGroupedTaskInfo;
// Keeps track of the previously known visible tasks for purposes of loading/unloading task data
private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
@@ -586,9 +583,11 @@ public abstract class RecentsView<
private final ViewPool<GroupedTaskView> mGroupedTaskViewPool;
private final ViewPool<DesktopTaskView> mDesktopTaskViewPool;
- private final TaskOverlayFactory mTaskOverlayFactory;
+ protected final TaskOverlayFactory mTaskOverlayFactory;
protected boolean mDisallowScrollToClearAll;
+ // True if it is not allowed to scroll to [AddDesktopButton].
+ protected boolean mDisallowScrollToAddDesk;
private boolean mOverlayEnabled;
protected boolean mFreezeViewVisibility;
private boolean mOverviewGridEnabled;
@@ -673,7 +672,7 @@ public abstract class RecentsView<
return;
}
Log.d(TAG, "onTaskRemoved: " + taskId);
- Task.TaskKey taskKey = taskContainer.getTask().key;
+ TaskKey taskKey = taskContainer.getTask().key;
UI_HELPER_EXECUTOR.execute(new CancellableTask<>(
() -> PackageManagerWrapper.getInstance()
.getActivityInfo(taskKey.getComponent(), taskKey.userId) == null,
@@ -832,7 +831,7 @@ public abstract class RecentsView<
mOrientationState.setMultiWindowMode(inMultiWindowMode);
setLayoutRotation(mOrientationState.getTouchRotation(),
mOrientationState.getDisplayRotation());
- updateChildTaskOrientations();
+ mUtils.updateChildTaskOrientations();
if (!inMultiWindowMode && mOverviewStateEnabled) {
// TODO: Re-enable layout transitions for addition of the unpinned task
reloadIfNeeded();
@@ -860,7 +859,7 @@ public abstract class RecentsView<
*/
private boolean mAnyTaskHasBeenDismissed;
- private final RecentsViewModel mRecentsViewModel;
+ protected final RecentsViewModel mRecentsViewModel;
private final RecentsViewModelHelper mHelper;
protected final RecentsViewUtils mUtils = new RecentsViewUtils(this);
protected final RecentsDismissUtils mDismissUtils = new RecentsDismissUtils(this);
@@ -868,6 +867,9 @@ public abstract class RecentsView<
private final Matrix mTmpMatrix = new Matrix();
private int mTaskViewCount = 0;
+
+ protected final BlurUtils mBlurUtils = new BlurUtils(this);
+
@Nullable
public TaskView getFirstTaskView() {
return mUtils.getFirstTaskView();
@@ -1593,7 +1595,7 @@ public abstract class RecentsView<
/**
* Returns true if the given TaskView is in expected scroll position.
*/
- public boolean isTaskInExpectedScrollPosition(TaskView taskView) {
+ public boolean isTaskInExpectedScrollPosition(@NonNull TaskView taskView) {
return getScrollForPage(indexOfChild(taskView))
== getPagedOrientationHandler().getPrimaryScroll(this);
}
@@ -2087,7 +2089,7 @@ public abstract class RecentsView<
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.layouts");
updateTaskSize();
- updateChildTaskOrientations();
+ mUtils.updateChildTaskOrientations();
traceEnd(Trace.TRACE_TAG_APP);
TaskView newRunningTaskView = mUtils.getDesktopTaskViewForDeskId(runningTaskViewDeskId);
@@ -2099,12 +2101,12 @@ public abstract class RecentsView<
if (newRunningTaskView != null) {
setRunningTaskViewId(newRunningTaskView.getTaskViewId());
} else {
- if (mActiveGestureRunningTasks != null) {
+ if (mActiveGestureGroupedTaskInfo != null) {
// This will update mRunningTaskViewId and create a stub view if necessary.
// We try to avoid this because it can cause a scroll jump, but it is needed
// for cases where the running task isn't included in this load plan (e.g. if
// the current running task is excludedFromRecents.)
- showCurrentTask(mActiveGestureRunningTasks, "applyLoadPlan");
+ showCurrentTask(mActiveGestureGroupedTaskInfo, "applyLoadPlan");
newRunningTaskView = getRunningTaskView();
} else {
setRunningTaskViewId(INVALID_TASK_ID);
@@ -2339,7 +2341,7 @@ public abstract class RecentsView<
updateSizeAndPadding();
// Update TaskView's DeviceProfile dependent layout.
- updateChildTaskOrientations();
+ mUtils.updateChildTaskOrientations();
requestLayout();
// Reapply the current page to update page scrolls.
@@ -2415,21 +2417,28 @@ public abstract class RecentsView<
}
/**
- * Sets the last TaskView selected.
+ * Returns the currently selected TaskView in Select mode.
+ */
+ @Nullable
+ public TaskView getSelectedTaskView() {
+ return mUtils.getSelectedTaskView();
+ }
+
+ /**
+ * Sets the selected TaskView in Select mode.
*/
public void setSelectedTask(int lastSelectedTaskId) {
- mSelectedTask = getTaskViewByTaskId(lastSelectedTaskId);
+ mUtils.setSelectedTaskView(getTaskViewByTaskId(lastSelectedTaskId));
}
/**
* Returns the bounds of the task selected to enter modal state.
*/
public Rect getSelectedTaskBounds() {
- if (mSelectedTask == null) {
- return enableGridOnlyOverview() && mContainer.getDeviceProfile().isTablet
- ? mLastComputedGridTaskSize : mLastComputedTaskSize;
+ if (getSelectedTaskView() == null) {
+ return mLastComputedTaskSize;
}
- return getTaskBounds(mSelectedTask);
+ return getTaskBounds(getSelectedTaskView());
}
/**
@@ -2445,7 +2454,7 @@ public abstract class RecentsView<
return deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f;
}
- private Rect getTaskBounds(TaskView taskView) {
+ protected Rect getTaskBounds(TaskView taskView) {
int selectedPage = indexOfChild(taskView);
int primaryScroll = getPagedOrientationHandler().getPrimaryScroll(this);
int selectedPageScroll = getScrollForPage(selectedPage);
@@ -2747,9 +2756,7 @@ public abstract class RecentsView<
}
setEnableDrawingLiveTile(false);
}
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false));
-
+ mBlurUtils.setDrawLiveTileBelowRecents(false);
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
post(this::onReset);
@@ -2820,7 +2827,7 @@ public abstract class RecentsView<
* Handle the edge case where Recents could increment task count very high over long
* period of device usage. Probably will never happen, but meh.
*/
- private TaskView getTaskViewFromPool(TaskViewType type) {
+ protected TaskView getTaskViewFromPool(TaskViewType type) {
TaskView taskView;
switch (type) {
case GROUPED:
@@ -2874,10 +2881,9 @@ public abstract class RecentsView<
*/
// TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity` from being
// considered in Overview.
- public void onGestureAnimationStart(Task[] runningTasks) {
- Log.d(TAG, "onGestureAnimationStart - runningTasks: " + Arrays.toString(runningTasks));
- mActiveGestureRunningTasks = runningTasks;
-
+ public void onGestureAnimationStart(GroupedTaskInfo groupedTaskInfo) {
+ Log.d(TAG, "onGestureAnimationStart - groupedTaskInfo: " + groupedTaskInfo);
+ mActiveGestureGroupedTaskInfo = groupedTaskInfo;
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
@@ -2887,7 +2893,7 @@ public abstract class RecentsView<
updateSizeAndPadding();
}
- showCurrentTask(mActiveGestureRunningTasks, "onGestureAnimationStart");
+ showCurrentTask(groupedTaskInfo, "onGestureAnimationStart");
setEnableFreeScroll(false);
setEnableDrawingLiveTile(false);
setRunningTaskHidden(true);
@@ -2902,7 +2908,7 @@ public abstract class RecentsView<
}
private boolean isGestureActive() {
- return mActiveGestureRunningTasks != null;
+ return mActiveGestureGroupedTaskInfo != null;
}
/**
@@ -2949,22 +2955,6 @@ public abstract class RecentsView<
return as;
}
- private void updateChildTaskOrientations() {
- for (TaskView taskView : getTaskViews()) {
- taskView.setOrientationState(mOrientationState);
- }
- boolean shouldRotateMenuForFakeRotation =
- !mOrientationState.isRecentsActivityRotationAllowed();
- if (!shouldRotateMenuForFakeRotation) {
- return;
- }
- AbstractFloatingView floatingView = getTopOpenViewWithType(mContainer, TYPE_TASK_MENU);
- if (floatingView instanceof TaskMenuView taskMenuView) {
- // Rotation is supported on phone (details at b/254198019#comment4)
- taskMenuView.onRotationChanged();
- }
- }
-
/**
* Called when a gesture from an app has finished, and an end target has been determined.
*/
@@ -3056,7 +3046,7 @@ public abstract class RecentsView<
* Called when a gesture from an app has finished, and the animation to the target has ended.
*/
public void onGestureAnimationEnd() {
- mActiveGestureRunningTasks = null;
+ mActiveGestureGroupedTaskInfo = null;
if (mOrientationState.setGestureActive(false)) {
updateOrientationHandler(/* forceRecreateDragLayerControllers = */ false);
}
@@ -3095,10 +3085,17 @@ public abstract class RecentsView<
/**
* Returns true if we should add a stub taskView for the running task id
*/
- protected boolean shouldAddStubTaskView(Task[] runningTasks) {
- int[] runningTaskIds = Arrays.stream(runningTasks).mapToInt(task -> task.key.id).toArray();
+ protected boolean shouldAddStubTaskView(GroupedTaskInfo groupedTaskInfo) {
+ int[] runningTaskIds;
+ if (groupedTaskInfo != null) {
+ runningTaskIds = groupedTaskInfo.getTaskInfoList().stream().mapToInt(
+ taskInfo -> taskInfo.taskId).toArray();
+ } else {
+ runningTaskIds = new int[0];
+ }
TaskView matchingTaskView = null;
- if (hasDesktopTask(runningTasks) && runningTaskIds.length == 1) {
+ if (groupedTaskInfo != null && groupedTaskInfo.isBaseType(GroupedTaskInfo.TYPE_DESK)
+ && runningTaskIds.length == 1) {
// TODO(b/342635213): Unsure if it's expected, desktop runningTasks only have a single
// taskId, therefore we match any DesktopTaskView that contains the runningTaskId.
TaskView taskview = getTaskViewByTaskId(runningTaskIds[0]);
@@ -3112,52 +3109,36 @@ public abstract class RecentsView<
}
/**
- * Creates a `DesktopTaskView` for the currently active desk on this display, which contains the
- * gievn `runningTasks`.
- */
- private DesktopTaskView createDesktopTaskViewForActiveDesk(Task[] runningTasks) {
- final int activeDeskId = mUtils.getActiveDeskIdOnThisDisplay();
- final var desktopTaskView = (DesktopTaskView) getTaskViewFromPool(TaskViewType.DESKTOP);
-
- // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity`.
- desktopTaskView.bind(
- new DesktopTask(activeDeskId, Arrays.asList(runningTasks)),
- mOrientationState, mTaskOverlayFactory);
- return desktopTaskView;
- }
-
- /**
- * Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
+ * Creates a task view (if necessary) to represent the tasks with the {@param groupedTaskInfo}.
*
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
* is called. Also scrolls the view to this task.
*/
- private void showCurrentTask(Task[] runningTasks, String caller) {
- Log.d(TAG, "showCurrentTask(" + caller + ") - runningTasks: "
- + Arrays.toString(runningTasks));
- if (runningTasks.length == 0) {
+ private void showCurrentTask(GroupedTaskInfo groupedTaskInfo, String caller) {
+ Log.d(TAG, "showCurrentTask(" + caller + ") - groupedTaskInfo: " + groupedTaskInfo);
+ if (groupedTaskInfo == null) {
return;
}
int runningTaskViewId = -1;
- if (shouldAddStubTaskView(runningTasks)) {
+ if (shouldAddStubTaskView(groupedTaskInfo)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView;
- final boolean needGroupTaskView = runningTasks.length > 1;
- final boolean needDesktopTask = hasDesktopTask(runningTasks);
- if (needDesktopTask) {
- taskView = createDesktopTaskViewForActiveDesk(runningTasks);
- } else if (needGroupTaskView) {
+ if (groupedTaskInfo.isBaseType(GroupedTaskInfo.TYPE_DESK)) {
+ taskView = mUtils.createDesktopTaskViewForActiveDesk(groupedTaskInfo);
+ } else if (groupedTaskInfo.isBaseType(GroupedTaskInfo.TYPE_SPLIT)) {
taskView = getTaskViewFromPool(TaskViewType.GROUPED);
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
// the actual app running we won't need to show the thumbnail until all the tasks
// load later anyways
- ((GroupedTaskView) taskView).bind(runningTasks[0], runningTasks[1],
- mOrientationState, mTaskOverlayFactory, mSplitBoundsConfig);
+ ((GroupedTaskView) taskView).bind(Task.from(groupedTaskInfo.getTaskInfo1()),
+ Task.from(groupedTaskInfo.getTaskInfo2()), mOrientationState,
+ mTaskOverlayFactory, mSplitBoundsConfig);
} else {
taskView = getTaskViewFromPool(TaskViewType.SINGLE);
- taskView.bind(runningTasks[0], mOrientationState, mTaskOverlayFactory);
+ taskView.bind(Task.from(groupedTaskInfo.getTaskInfo1()), mOrientationState,
+ mTaskOverlayFactory);
}
if (mAddDesktopButton != null && wasEmpty) {
addView(mAddDesktopButton);
@@ -3174,7 +3155,7 @@ public abstract class RecentsView<
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
} else {
- var runningTaskView = getTaskViewByTaskId(runningTasks[0].key.id);
+ var runningTaskView = getTaskViewByTaskId(groupedTaskInfo.getTaskInfo1().taskId);
if (runningTaskView != null) {
runningTaskViewId = runningTaskView.getTaskViewId();
}
@@ -3201,28 +3182,12 @@ public abstract class RecentsView<
setRunningTaskHidden(runningTaskTileHidden);
// Update task size after setting current task.
updateTaskSize();
- updateChildTaskOrientations();
+ mUtils.updateChildTaskOrientations();
// Reload the task list
reloadIfNeeded();
}
- private boolean hasDesktopTask(Task[] runningTasks) {
- if (!DesktopModeStatus.canEnterDesktopMode(mContext)) {
- return false;
- }
- for (Task task : runningTasks) {
- if (task.key.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) {
- return true;
- }
- }
-
- // A running empty desk will have a single running app for the `DesktopWallpaperActivity`.
- // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity`.
-
- return false;
- }
-
/**
* Sets the running task id, cleaning up the old running task if necessary.
*/
@@ -3375,8 +3340,8 @@ public abstract class RecentsView<
int snappedPage = isKeyboardTaskFocusPending() ? mKeyboardTaskFocusIndex : getNextPage();
TaskView snappedTaskView = getTaskViewAt(snappedPage);
TaskView homeTaskView = getHomeTaskView();
- TaskView expectedCurrentTaskView = mUtils.getExpectedCurrentTask(getFocusedTaskView(),
- getRunningTaskView());
+ TaskView expectedCurrentTaskView = mUtils.getExpectedCurrentTask(getRunningTaskView(),
+ getFocusedTaskView());
TaskView nextFocusedTaskView = null;
// Don't clear the top row, if the user has dismissed a task, to maintain the task order.
@@ -3951,8 +3916,10 @@ public abstract class RecentsView<
newClearAllShortTotalWidthTranslation = expectedFirstTaskStart - firstTaskStart;
}
}
- if (lastGridTaskView != null && (lastGridTaskView.isVisibleToUser() || (
- isExpressiveDismiss && lastGridTaskView == dismissedTaskView))) {
+ if (lastGridTaskView != null && (
+ (!isExpressiveDismiss && lastGridTaskView.isVisibleToUser()) || (isExpressiveDismiss
+ && (isTaskViewVisible(lastGridTaskView)
+ || lastGridTaskView == dismissedTaskView)))) {
// After dismissal, animate translation of the remaining tasks to fill any gap left
// between the end of the grid and the clear all button. Only animate if the clear
// all button is visible or would become visible after dismissal.
@@ -4354,7 +4321,7 @@ public abstract class RecentsView<
finalNextFocusedTaskView.getDismissIconFadeInAnimator().start();
}
updateTaskSize();
- updateChildTaskOrientations();
+ mUtils.updateChildTaskOrientations();
// Update scroll and snap to page.
updateScrollSynchronously();
@@ -4748,11 +4715,12 @@ public abstract class RecentsView<
runDismissAnimation(pa);
}
- protected void expressiveDismissTaskView(TaskView taskView) {
+ protected void expressiveDismissTaskView(TaskView taskView, Function0<Unit> onEndRunnable) {
PendingAnimation pa = new PendingAnimation(DISMISS_TASK_DURATION);
createTaskDismissAnimation(pa, taskView, false /* animateTaskView */, true /* removeTask */,
DISMISS_TASK_DURATION, false /* dismissingForSplitSelection*/,
true /* isExpressiveDismiss */);
+ pa.addEndListener((success) -> onEndRunnable.invoke());
runDismissAnimation(pa);
}
@@ -5025,15 +4993,8 @@ public abstract class RecentsView<
}
private void updatePivots() {
- if (mOverviewSelectEnabled) {
- if (enableGridOnlyOverview()) {
- getModalTaskSize(mTempRect);
- Rect selectedTaskPosition = getSelectedTaskBounds();
- Utilities.getPivotsForScalingRectToRect(mTempRect, selectedTaskPosition,
- mTempPointF);
- } else {
- mTempPointF.set(mLastComputedTaskSize.centerX(), mLastComputedTaskSize.bottom);
- }
+ if (mOverviewSelectEnabled && !enableGridOnlyOverview()) {
+ mTempPointF.set(mLastComputedTaskSize.centerX(), mLastComputedTaskSize.bottom);
} else {
mTempRect.set(mLastComputedTaskSize);
getPagedViewOrientedState().getFullScreenScaleAndPivot(mTempRect,
@@ -5079,7 +5040,7 @@ public abstract class RecentsView<
&& (enableGridOnlyOverview() || enableLargeDesktopWindowingTile())
&& mTaskModalness > 0;
if (shouldCalculateOffsetForAllTasks) {
- modalMidpoint = indexOfChild(mSelectedTask);
+ modalMidpoint = indexOfChild(getSelectedTaskView());
}
float midpointOffsetSize = 0;
@@ -5287,7 +5248,7 @@ public abstract class RecentsView<
*/
private float getVerticalOffsetSize(TaskView taskView, float offsetProgress) {
if (offsetProgress == 0 || !(showAsGrid() && enableGridOnlyOverview())
- || mSelectedTask == null) {
+ || getSelectedTaskView() == null) {
// Don't bother calculating everything below if we won't offset vertically.
return 0;
}
@@ -5295,7 +5256,7 @@ public abstract class RecentsView<
// First, get the position of the task relative to the top row.
Rect taskPosition = getTaskBounds(taskView);
- boolean isSelectedTaskTopRow = mTopRowIdSet.contains(mSelectedTask.getTaskViewId());
+ boolean isSelectedTaskTopRow = mTopRowIdSet.contains(getSelectedTaskView().getTaskViewId());
boolean isChildTopRow = mTopRowIdSet.contains(taskView.getTaskViewId());
// Whether the task should be shifted to the top.
boolean isTopShift = !isSelectedTaskTopRow && isChildTopRow;
@@ -5349,8 +5310,8 @@ public abstract class RecentsView<
* Resets the visuals when exit modal state.
*/
public void resetModalVisuals() {
- if (mSelectedTask != null) {
- mSelectedTask.taskContainers.forEach(
+ if (getSelectedTaskView() != null) {
+ getSelectedTaskView().taskContainers.forEach(
taskContainer -> taskContainer.getOverlay().resetModalVisuals());
}
}
@@ -5819,12 +5780,10 @@ public abstract class RecentsView<
// above RecentsView to avoid wallpaper blur from being applied to it.
if (!taskView.isRunningTask()) {
runActionOnRemoteHandles(
- remoteTargetHandle -> {
- remoteTargetHandle.getTaskViewSimulator().setPivotOverride(
- mTempPointF);
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(
- false);
- });
+ remoteTargetHandle ->
+ remoteTargetHandle.getTaskViewSimulator()
+ .setPivotOverride(mTempPointF));
+ mBlurUtils.setDrawLiveTileBelowRecents(false);
}
}
@@ -5959,8 +5918,7 @@ public abstract class RecentsView<
mPendingAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false));
+ mBlurUtils.setDrawLiveTileBelowRecents(false);
}
});
mPendingAnimation.addEndListener(isSuccess -> {
@@ -5998,8 +5956,7 @@ public abstract class RecentsView<
// If launch animation didn't complete i.e. user dragged live tile down and then
// back up and returned to Overview, then we need to ensure we reset the
// view to draw below recents so that it can't be interacted with.
- runActionOnRemoteHandles(remoteTargetHandle ->
- remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
+ mBlurUtils.setDrawLiveTileBelowRecents(true);
redrawLiveTile();
}
return Unit.INSTANCE;
@@ -6011,6 +5968,9 @@ public abstract class RecentsView<
updateCurrentTaskActionsVisibility();
loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
updateEnabledOverlays();
+ if (enableRefactorTaskThumbnail()) {
+ mUtils.updateCentralTask();
+ }
}
@Override
@@ -6078,6 +6038,7 @@ public abstract class RecentsView<
});
}
+ @Nullable
public RemoteTargetHandle[] getRemoteTargetHandles() {
return mRemoteTargetHandles;
}
@@ -6231,6 +6192,9 @@ public abstract class RecentsView<
mRecentsAnimationController = null;
mSplitSelectStateController.setRecentsAnimationRunning(false);
executeSideTaskLaunchCallback();
+ if (enableOverviewBackgroundWallpaperBlur()) {
+ mBlurUtils.setDrawLiveTileBelowRecents(false);
+ }
}
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
@@ -6239,6 +6203,17 @@ public abstract class RecentsView<
updateMinAndMaxScrollX();
}
}
+ /**
+ * Update the value of [mDisallowScrollToAddDesk]
+ */
+ public void setDisallowScrollToAddDesk(boolean disallowScrollToAddDesk) {
+ if (mDisallowScrollToAddDesk != disallowScrollToAddDesk) {
+ mDisallowScrollToAddDesk = disallowScrollToAddDesk;
+ updateMinAndMaxScrollX();
+ }
+ }
+
+
/**
* Updates page scroll synchronously after measure and layout child views.
@@ -6384,7 +6359,20 @@ public abstract class RecentsView<
if (addDesktopButtonIndex >= 0 && addDesktopButtonIndex < outPageScrolls.length) {
int firstViewIndex = getFirstViewIndex();
if (firstViewIndex >= 0 && firstViewIndex < outPageScrolls.length) {
- outPageScrolls[addDesktopButtonIndex] = outPageScrolls[firstViewIndex];
+ // If we can scroll to [AddDesktopButton], make its page scroll equal to
+ // the first [TaskView]. Otherwise, make its page scroll out of range of
+ // [minScroll, maxScroll].
+ if (!mDisallowScrollToAddDesk) {
+ outPageScrolls[addDesktopButtonIndex] = outPageScrolls[firstViewIndex];
+ } else {
+ outPageScrolls[addDesktopButtonIndex] =
+ outPageScrolls[firstViewIndex] + (mIsRtl ? 1 : -1);
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "getPageScrolls - addDesktopButtonScroll: "
+ + outPageScrolls[addDesktopButtonIndex]);
}
}
if (DEBUG) {
@@ -6707,8 +6695,14 @@ public abstract class RecentsView<
private void setTaskModalness(float modalness) {
mTaskModalness = modalness;
updatePageOffsets();
- if (mSelectedTask != null) {
- mSelectedTask.setModalness(modalness);
+ if (getSelectedTaskView() != null) {
+ if (enableGridOnlyOverview()) {
+ for (TaskView taskView : getTaskViews()) {
+ taskView.setModalness(modalness);
+ }
+ } else {
+ getSelectedTaskView().setModalness(modalness);
+ }
} else if (getCurrentPageTaskView() != null) {
getCurrentPageTaskView().setModalness(modalness);
}
@@ -7026,7 +7020,7 @@ public abstract class RecentsView<
// `AddNewDesktopButton`.
DesktopTaskView desktopTaskView =
(DesktopTaskView) getTaskViewFromPool(TaskViewType.DESKTOP);
- desktopTaskView.bind(new DesktopTask(deskId, new ArrayList<>()),
+ desktopTaskView.bind(new DesktopTask(deskId, displayId, new ArrayList<>()),
mOrientationState, mTaskOverlayFactory);
Objects.requireNonNull(mAddDesktopButton);
@@ -7034,7 +7028,7 @@ public abstract class RecentsView<
addView(desktopTaskView, insertionIndex);
updateTaskSize();
- updateChildTaskOrientations();
+ mUtils.updateChildTaskOrientations();
// TODO: b/401002178 - Recalculate the new current page such that the addition of the new
// desk does not result in a change in the current scroll page.
@@ -7170,10 +7164,10 @@ public abstract class RecentsView<
* spring in response to the perceived impact of the settling task.
*/
public SpringAnimation createTaskDismissSettlingSpringAnimation(TaskView draggedTaskView,
- float velocity, boolean isDismissing, SingleAxisSwipeDetector detector,
- int dismissLength, Function0<Unit> onEndRunnable) {
+ float velocity, boolean isDismissing, int dismissLength,
+ Function0<Unit> onEndRunnable) {
return mDismissUtils.createTaskDismissSettlingSpringAnimation(draggedTaskView, velocity,
- isDismissing, detector, dismissLength, onEndRunnable);
+ isDismissing, dismissLength, onEndRunnable);
}
/**
@@ -7186,4 +7180,15 @@ public abstract class RecentsView<
public interface TaskLaunchListener {
void onTaskLaunched();
}
+
+ /**
+ * Sets whether the remote animation targets should draw below the recents view.
+ *
+ * @param drawBelowRecents whether the surface should draw below Recents.
+ * @param remoteTargetHandles collection of remoteTargetHandles in Recents.
+ */
+ public void setDrawBelowRecents(boolean drawBelowRecents,
+ RemoteTargetHandle[] remoteTargetHandles) {
+ mBlurUtils.setDrawBelowRecents(drawBelowRecents, remoteTargetHandles);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 24b7fa7c81..b265b13393 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -16,15 +16,21 @@
package com.android.quickstep.views
+import android.graphics.PointF
import android.graphics.Rect
import android.util.FloatProperty
import android.view.KeyEvent
import android.view.View
+import android.view.View.LAYOUT_DIRECTION_LTR
+import android.view.View.LAYOUT_DIRECTION_RTL
import androidx.core.view.children
-import com.android.launcher3.AbstractFloatingView
-import com.android.launcher3.Flags
+import com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU
+import com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType
+import com.android.launcher3.Flags.enableGridOnlyOverview
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
+import com.android.launcher3.Flags.enableOverviewIconMenu
import com.android.launcher3.Flags.enableSeparateExternalDisplayTasks
+import com.android.launcher3.Utilities.getPivotsForScalingRectToRect
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.statehandlers.DesktopVisibilityController.Companion.INACTIVE_DESK_ID
import com.android.launcher3.util.IntArray
@@ -33,8 +39,11 @@ import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
import com.android.quickstep.util.isExternalDisplay
import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA
+import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.wm.shell.shared.GroupedTaskInfo
import java.util.function.BiConsumer
+import kotlin.math.min
import kotlin.reflect.KMutableProperty1
/**
@@ -357,24 +366,117 @@ class RecentsViewUtils(private val recentsView: RecentsView<*, *>) {
}
}
+ private fun getTaskMenu(): TaskMenuView? =
+ getTopOpenViewWithType(recentsView.mContainer, TYPE_TASK_MENU) as? TaskMenuView
+
fun shouldInterceptKeyEvent(event: KeyEvent): Boolean {
- if (Flags.enableOverviewIconMenu()) {
- val floatingView: AbstractFloatingView? = AbstractFloatingView.getTopOpenViewWithType(
- recentsView.mContainer as RecentsViewContainer,
- AbstractFloatingView.TYPE_TASK_MENU
- )
- val isMenuOpen = floatingView?.isOpen
- return isMenuOpen == true || event.keyCode == KeyEvent.KEYCODE_TAB
+ if (enableOverviewIconMenu()) {
+ return getTaskMenu()?.isOpen == true || event.keyCode == KeyEvent.KEYCODE_TAB
}
return false
}
+ fun updateChildTaskOrientations() {
+ with(recentsView) {
+ taskViews.forEach { it.setOrientationState(mOrientationState) }
+ if (enableOverviewIconMenu()) {
+ children.forEach {
+ it.layoutDirection = if (isRtl) LAYOUT_DIRECTION_LTR else LAYOUT_DIRECTION_RTL
+ }
+ }
+
+ // Return when it's not fake landscape
+ if (mOrientationState.isRecentsActivityRotationAllowed) return@with
+
+ // Rotation is supported on phone (details at b/254198019#comment4)
+ getTaskMenu()?.onRotationChanged()
+ }
+ }
+
+ fun updateCentralTask() {
+ val isTablet: Boolean = getDeviceProfile().isTablet
+ val actionsViewCanRelateToTaskView = !(isTablet && enableGridOnlyOverview())
+ val focusedTaskView = recentsView.focusedTaskView
+ val currentPageTaskView = recentsView.currentPageTaskView
+
+ fun isInExpectedScrollPosition(taskView: TaskView?) =
+ taskView?.let { recentsView.isTaskInExpectedScrollPosition(it) } ?: false
+
+ val centralTaskIds: Set<Int> =
+ when {
+ !actionsViewCanRelateToTaskView -> emptySet()
+ isTablet && isInExpectedScrollPosition(focusedTaskView) ->
+ focusedTaskView!!.taskIdSet
+ isInExpectedScrollPosition(currentPageTaskView) -> currentPageTaskView!!.taskIdSet
+ else -> emptySet()
+ }
+
+ recentsView.mRecentsViewModel.updateCentralTaskIds(centralTaskIds)
+ }
+
var deskExplodeProgress: Float = 0f
set(value) {
field = value
taskViews.filterIsInstance<DesktopTaskView>().forEach { it.explodeProgress = field }
}
+ var selectedTaskView: TaskView? = null
+ set(newValue) {
+ val oldValue = field
+ field = newValue
+ if (oldValue != newValue) {
+ onSelectedTaskViewUpdated(oldValue, newValue)
+ }
+ }
+
+ private fun onSelectedTaskViewUpdated(
+ oldSelectedTaskView: TaskView?,
+ newSelectedTaskView: TaskView?,
+ ) {
+ if (!enableGridOnlyOverview()) return
+ with(recentsView) {
+ oldSelectedTaskView?.modalScale = 1f
+ oldSelectedTaskView?.modalPivot = null
+
+ if (newSelectedTaskView == null) return
+
+ val modalTaskBounds = mTempRect
+ getModalTaskSize(modalTaskBounds)
+ val selectedTaskBounds = getTaskBounds(newSelectedTaskView)
+
+ // Map bounds to selectedTaskView's coordinate system.
+ modalTaskBounds.offset(-selectedTaskBounds.left, -selectedTaskBounds.top)
+ selectedTaskBounds.offset(-selectedTaskBounds.left, -selectedTaskBounds.top)
+
+ val modalScale =
+ min(
+ (modalTaskBounds.height().toFloat() / selectedTaskBounds.height()),
+ (modalTaskBounds.width().toFloat() / selectedTaskBounds.width()),
+ )
+ val modalPivot = PointF()
+ getPivotsForScalingRectToRect(modalTaskBounds, selectedTaskBounds, modalPivot)
+
+ newSelectedTaskView.modalScale = modalScale
+ newSelectedTaskView.modalPivot = modalPivot
+ }
+ }
+
+ /**
+ * Creates a [DesktopTaskView] for the currently active desk on this display, which contains the
+ * tasks with the given [groupedTaskInfo].
+ */
+ fun createDesktopTaskViewForActiveDesk(groupedTaskInfo: GroupedTaskInfo): DesktopTaskView {
+ val desktopTaskView =
+ recentsView.getTaskViewFromPool(TaskViewType.DESKTOP) as DesktopTaskView
+ val tasks: List<Task> = groupedTaskInfo.taskInfoList.map { taskInfo -> Task.from(taskInfo) }
+ desktopTaskView.bind(
+ DesktopTask(groupedTaskInfo.deskId, groupedTaskInfo.deskDisplayId, tasks),
+ recentsView.mOrientationState,
+ recentsView.mTaskOverlayFactory,
+ )
+ return desktopTaskView
+ }
+
companion object {
class RecentsViewFloatProperty(
private val utilsProperty: KMutableProperty1<RecentsViewUtils, Float>
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index afe7e928ad..0e769d0d37 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -22,6 +22,7 @@ import android.util.Log
import android.view.View
import android.view.View.OnClickListener
import com.android.app.tracing.traceSection
+import com.android.launcher3.Flags.enableOverviewIconMenu
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.model.data.TaskViewItemInfo
import com.android.launcher3.util.SplitConfigurationOptions
@@ -31,7 +32,6 @@ import com.android.quickstep.ViewUtils.addAccessibleChildToList
import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
import com.android.quickstep.recents.ui.mapper.TaskUiStateMapper
import com.android.quickstep.recents.ui.viewmodel.TaskData
-import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
@@ -40,7 +40,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData
class TaskContainer(
val taskView: TaskView,
val task: Task,
- val taskContentView: TaskContentView,
val snapshotView: View,
val iconView: TaskViewIcon,
/**
@@ -112,8 +111,8 @@ class TaskContainer(
fun destroy() =
traceSection("TaskContainer.destroy") {
digitalWellBeingToast?.destroy()
- taskContentView.scaleX = 1f
- taskContentView.scaleY = 1f
+ snapshotView.scaleX = 1f
+ snapshotView.scaleY = 1f
overlay.reset()
if (enableRefactorTaskThumbnail()) {
isThumbnailValid = false
@@ -122,6 +121,10 @@ class TaskContainer(
} else {
thumbnailViewDeprecated.setShowSplashForSplitSelection(false)
}
+
+ if (enableOverviewIconMenu()) {
+ (iconView as IconAppChipView).reset()
+ }
}
fun setOverlayEnabled(enabled: Boolean) {
@@ -174,9 +177,13 @@ class TaskContainer(
clickCloseListener: OnClickListener?,
) =
traceSection("TaskContainer.setState") {
- taskContentView.setState(
- TaskUiStateMapper.toTaskHeaderState(state, hasHeader, clickCloseListener),
- TaskUiStateMapper.toTaskThumbnailUiState(state, liveTile),
+ thumbnailView.setState(
+ TaskUiStateMapper.toTaskThumbnailUiState(
+ state,
+ liveTile,
+ hasHeader,
+ clickCloseListener,
+ ),
state?.taskId,
)
thumbnailData = if (state is TaskData.Data) state.thumbnailData else null
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
index 696f934701..f7cb7bef78 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
@@ -251,6 +251,7 @@ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int = 0) :
rotation = orientationHandler.degreesRotated
if (enableOverviewIconMenu()) {
+ elevation = resources.getDimension(R.dimen.task_thumbnail_icon_menu_elevation)
translationX = thumbnailAlignedX
translationY = thumbnailAlignedY
} else {
@@ -428,9 +429,7 @@ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int = 0) :
var additionalTranslationX = 0f
if (
- recentsViewContainer.deviceProfile.isLandscape &&
- taskContainer.stagePosition ==
- SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+ taskContainer.stagePosition == SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
) {
// Animate menu and icon when split task would display off the side of the screen.
additionalTranslationX =
@@ -474,8 +473,8 @@ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int = 0) :
val isLastMenuOptionFocused =
optionLayout.indexOfChild(optionLayout.focusedChild) == optionLayout.childCount - 1
if (
- (isLastMenuOptionFocused && event.keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
- || (isFirstMenuOptionFocused && event.keyCode == KeyEvent.KEYCODE_DPAD_UP)
+ (isLastMenuOptionFocused && event.keyCode == KeyEvent.KEYCODE_DPAD_DOWN) ||
+ (isFirstMenuOptionFocused && event.keyCode == KeyEvent.KEYCODE_DPAD_UP)
) {
iconView.requestFocus()
return true
diff --git a/quickstep/src/com/android/quickstep/views/TaskHeaderView.kt b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
index 1fda5a3ace..9a8805bf0f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskHeaderView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
@@ -18,33 +18,23 @@ package com.android.quickstep.views
import android.content.Context
import android.util.AttributeSet
+import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.view.isGone
import com.android.launcher3.R
-import com.android.quickstep.task.thumbnail.TaskHeaderUiState
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
-class TaskHeaderView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
- ConstraintLayout(context, attrs) {
+class TaskThumbnailViewHeader
+@JvmOverloads
+constructor(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
private val headerTitleView: TextView by lazy { findViewById(R.id.header_app_title) }
private val headerIconView: ImageView by lazy { findViewById(R.id.header_app_icon) }
private val headerCloseButton: ImageButton by lazy { findViewById(R.id.header_close_button) }
- fun setState(taskHeaderState: TaskHeaderUiState) {
- when (taskHeaderState) {
- is TaskHeaderUiState.ShowHeader -> {
- setHeader(taskHeaderState.header)
- isGone = false
- }
- TaskHeaderUiState.HideHeader -> isGone = true
- }
- }
-
- private fun setHeader(header: TaskHeaderUiState.ThumbnailHeader) {
- headerTitleView.text = header.title
+ fun setHeader(header: ThumbnailHeader) {
+ headerTitleView.setText(header.title)
headerIconView.setImageDrawable(header.icon)
headerCloseButton.setOnClickListener(header.clickCloseListener)
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 8d95b131bf..fa3fd91615 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -33,6 +33,7 @@ import android.util.Log
import android.view.Display
import android.view.MotionEvent
import android.view.View
+import android.view.View.OnClickListener
import android.view.ViewGroup
import android.view.ViewStub
import android.view.accessibility.AccessibilityNodeInfo
@@ -77,6 +78,7 @@ import com.android.launcher3.util.rects.set
import com.android.quickstep.FullscreenDrawParams
import com.android.quickstep.RecentsModel
import com.android.quickstep.RemoteAnimationTargets
+import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.TaskViewUtils
import com.android.quickstep.orientation.RecentsPagedOrientationHandler
@@ -87,7 +89,6 @@ import com.android.quickstep.recents.domain.usecase.ThumbnailPosition
import com.android.quickstep.recents.ui.viewmodel.TaskData
import com.android.quickstep.recents.ui.viewmodel.TaskTileUiState
import com.android.quickstep.recents.ui.viewmodel.TaskViewModel
-import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.util.ActiveGestureErrorDetector
import com.android.quickstep.util.ActiveGestureLog
import com.android.quickstep.util.BorderAnimator
@@ -95,8 +96,11 @@ import com.android.quickstep.util.BorderAnimator.Companion.createSimpleBorderAni
import com.android.quickstep.util.RecentsOrientedState
import com.android.quickstep.util.TaskCornerRadius
import com.android.quickstep.util.TaskRemovedDuringLaunchListener
-import com.android.quickstep.util.displayId
import com.android.quickstep.util.isExternalDisplay
+import com.android.quickstep.util.safeDisplayId
+import com.android.quickstep.views.IconAppChipView.AppChipStatus
+import com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL
+import com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED
import com.android.quickstep.views.RecentsView.UNBOUND_TASK_VIEW_ID
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
@@ -146,8 +150,11 @@ constructor(
val isRunningTask: Boolean
get() = this === recentsView?.runningTaskView
- val displayId: Int
- get() = taskContainers.firstOrNull()?.task.displayId
+ private val isSelectedTask: Boolean
+ get() = this === recentsView?.selectedTaskView
+
+ open val displayId: Int
+ get() = taskContainers.firstOrNull()?.task.safeDisplayId
val isExternalDisplay: Boolean
get() = displayId.isExternalDisplay
@@ -336,6 +343,12 @@ constructor(
onModalnessUpdated(field)
}
+ var modalPivot: PointF? = null
+ set(value) {
+ field = value
+ updatePivots()
+ }
+
var splitSplashAlpha = 0f
set(value) {
field = value
@@ -360,6 +373,12 @@ constructor(
applyScale()
}
+ var modalScale = 1f
+ set(value) {
+ field = value
+ applyScale()
+ }
+
private var dismissTranslationX = 0f
set(value) {
field = value
@@ -445,9 +464,10 @@ constructor(
}
private val taskViewAlpha = MultiValueAlpha(this, Alpha.entries.size)
- protected var stableAlpha by MultiPropertyDelegate(taskViewAlpha, Alpha.STABLE)
- var attachAlpha by MultiPropertyDelegate(taskViewAlpha, Alpha.ATTACH)
- var splitAlpha by MultiPropertyDelegate(taskViewAlpha, Alpha.SPLIT)
+ protected var stableAlpha by MultiPropertyDelegate(taskViewAlpha, Alpha.Stable)
+ var attachAlpha by MultiPropertyDelegate(taskViewAlpha, Alpha.Attach)
+ var splitAlpha by MultiPropertyDelegate(taskViewAlpha, Alpha.Split)
+ private var modalAlpha by MultiPropertyDelegate(taskViewAlpha, Alpha.Modal)
protected var shouldShowScreenshot = false
get() = !isRunningTask || field
@@ -615,16 +635,19 @@ constructor(
super.draw(canvas)
}
+ override fun setLayoutDirection(layoutDirection: Int) {
+ super.setLayoutDirection(layoutDirection)
+ if (enableOverviewIconMenu()) {
+ val deviceLayoutDirection = resources.configuration.layoutDirection
+ taskContainers.forEach {
+ (it.iconView as IconAppChipView).layoutDirection = deviceLayoutDirection
+ }
+ }
+ }
+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
- val thumbnailTopMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
- if (container.deviceProfile.isTablet) {
- pivotX = (if (layoutDirection == LAYOUT_DIRECTION_RTL) 0 else right - left).toFloat()
- pivotY = thumbnailTopMargin.toFloat()
- } else {
- pivotX = (right - left) * 0.5f
- pivotY = thumbnailTopMargin + (height - thumbnailTopMargin) * 0.5f
- }
+ updatePivots()
systemGestureExclusionRects =
SYSTEM_GESTURE_EXCLUSION_RECT.onEach {
it.right = width
@@ -635,6 +658,24 @@ constructor(
}
}
+ private fun updatePivots() {
+ val modalPivot = modalPivot
+ if (modalPivot != null) {
+ pivotX = modalPivot.x
+ pivotY = modalPivot.y
+ } else {
+ val thumbnailTopMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
+ if (container.deviceProfile.isTablet) {
+ pivotX =
+ (if (layoutDirection == LAYOUT_DIRECTION_RTL) 0 else right - left).toFloat()
+ pivotY = thumbnailTopMargin.toFloat()
+ } else {
+ pivotX = (right - left) * 0.5f
+ pivotY = thumbnailTopMargin + (height - thumbnailTopMargin) * 0.5f
+ }
+ }
+ }
+
override fun onRecycle() {
resetPersistentViewTransforms()
@@ -642,6 +683,9 @@ constructor(
attachAlpha = 1f
splitAlpha = 1f
splitSplashAlpha = 0f
+ modalAlpha = 1f
+ modalScale = 1f
+ modalPivot = null
taskThumbnailSplashAlpha = 0f
// Clear any references to the thumbnail (it will be re-read either from the cache or the
// system on next bind)
@@ -722,10 +766,13 @@ constructor(
}
protected open fun inflateViewStubs() {
- findViewById<ViewStub>(R.id.task_content_view)
- ?.apply { layoutResource = R.layout.task_content_view }
+ findViewById<ViewStub>(R.id.snapshot)
+ ?.apply {
+ layoutResource =
+ if (enableRefactorTaskThumbnail()) R.layout.task_thumbnail
+ else R.layout.task_thumbnail_deprecated
+ }
?.inflate()
-
findViewById<ViewStub>(R.id.icon)
?.apply {
layoutResource =
@@ -786,6 +833,20 @@ constructor(
height = container.thumbnailView.height,
)
container.setOverlayEnabled(state.taskOverlayEnabled, thumbnailPosition)
+ if (state.isCentralTask) {
+ this.container.actionsView.let {
+ it.updateDisabledFlags(
+ DISABLED_ROTATED,
+ thumbnailPosition?.isRotated ?: false,
+ )
+ it.updateDisabledFlags(
+ DISABLED_NO_THUMBNAIL,
+ state.tasks.any { taskData ->
+ (taskData as? TaskData.Data)?.thumbnailData?.thumbnail == null
+ },
+ )
+ }
+ }
if (enableOverviewIconMenu()) {
setIconState(container, containerState)
@@ -858,7 +919,6 @@ constructor(
listOf(
createTaskContainer(
task,
- R.id.task_content_view,
R.id.snapshot,
R.id.icon,
R.id.show_windows,
@@ -893,9 +953,9 @@ constructor(
taskContainers.forEach { container ->
container.bind()
if (enableRefactorTaskThumbnail()) {
- container.taskContentView.cornerRadius =
+ container.thumbnailView.cornerRadius =
thumbnailFullscreenParams.currentCornerRadius
- container.taskContentView.doOnSizeChange { width, height ->
+ container.thumbnailView.doOnSizeChange { width, height ->
updateThumbnailValidity(container)
val thumbnailPosition = updateThumbnailMatrix(container, width, height)
container.refreshOverlay(thumbnailPosition)
@@ -922,7 +982,6 @@ constructor(
protected fun createTaskContainer(
task: Task,
- @IdRes taskContentViewId: Int,
@IdRes thumbnailViewId: Int,
@IdRes iconViewId: Int,
@IdRes showWindowViewId: Int,
@@ -932,12 +991,10 @@ constructor(
): TaskContainer =
traceSection("TaskView.createTaskContainer") {
val iconView = findViewById<View>(iconViewId) as TaskViewIcon
- val taskContentView = findViewById<TaskContentView>(taskContentViewId)
return TaskContainer(
this,
task,
- taskContentView,
- taskContentView.findViewById(thumbnailViewId),
+ findViewById(thumbnailViewId),
iconView,
TransformingTouchDelegate(iconView.asView()),
stagePosition,
@@ -1026,7 +1083,7 @@ constructor(
protected open fun updateThumbnailSize() {
// TODO(b/271468547), we should default to setting translations only on the snapshot instead
// of a hybrid of both margins and translations
- firstTaskContainer?.taskContentView?.updateLayoutParams<LayoutParams> {
+ firstTaskContainer?.snapshotView?.updateLayoutParams<LayoutParams> {
topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
}
taskContainers.forEach { it.digitalWellBeingToast?.setupLayout() }
@@ -1040,11 +1097,11 @@ constructor(
val thumbnailBounds = Rect()
if (relativeToDragLayer) {
container.dragLayer.getDescendantRectRelativeToSelf(
- it.taskContentView,
+ it.snapshotView,
thumbnailBounds,
)
} else {
- thumbnailBounds.set(it.taskContentView)
+ thumbnailBounds.set(it.snapshotView)
}
bounds.union(thumbnailBounds)
}
@@ -1210,15 +1267,14 @@ constructor(
/** Launch of the current task (both live and inactive tasks) with an animation. */
fun launchWithAnimation(): RunnableList? {
return if (isRunningTask && recentsView?.remoteTargetHandles != null) {
- launchAsLiveTile()
+ launchAsLiveTile(recentsView?.remoteTargetHandles!!)
} else {
launchAsStaticTile()
}
}
- private fun launchAsLiveTile(): RunnableList? {
+ private fun launchAsLiveTile(remoteTargetHandles: Array<RemoteTargetHandle>): RunnableList? {
val recentsView = recentsView ?: return null
- val remoteTargetHandles = recentsView.remoteTargetHandles
if (!isClickableAsLiveTile) {
Log.e(
TAG,
@@ -1228,21 +1284,27 @@ constructor(
}
isClickableAsLiveTile = false
val targets =
- if (remoteTargetHandles.size == 1) {
- remoteTargetHandles[0].transformParams.targetSet
+ if (remoteTargetHandles.isNotEmpty()) {
+ if (remoteTargetHandles.size == 1) {
+ remoteTargetHandles[0].transformParams.targetSet
+ } else {
+ val apps =
+ remoteTargetHandles.flatMap {
+ it.transformParams.targetSet.apps.asIterable()
+ }
+ val wallpapers =
+ remoteTargetHandles.flatMap {
+ it.transformParams.targetSet.wallpapers.asIterable()
+ }
+ RemoteAnimationTargets(
+ apps.toTypedArray(),
+ wallpapers.toTypedArray(),
+ remoteTargetHandles[0].transformParams.targetSet.nonApps,
+ remoteTargetHandles[0].transformParams.targetSet.targetMode,
+ )
+ }
} else {
- val apps =
- remoteTargetHandles.flatMap { it.transformParams.targetSet.apps.asIterable() }
- val wallpapers =
- remoteTargetHandles.flatMap {
- it.transformParams.targetSet.wallpapers.asIterable()
- }
- RemoteAnimationTargets(
- apps.toTypedArray(),
- wallpapers.toTypedArray(),
- remoteTargetHandles[0].transformParams.targetSet.nonApps,
- remoteTargetHandles[0].transformParams.targetSet.targetMode,
- )
+ null
}
if (targets == null) {
// If the recents animation is cancelled somehow between the parent if block and
@@ -1270,7 +1332,7 @@ constructor(
targets.apps,
targets.wallpapers,
targets.nonApps,
- true /* launcherClosing */,
+ true, /* launcherClosing */
recentsView.stateManager,
recentsView,
recentsView.depthController,
@@ -1484,10 +1546,11 @@ constructor(
}
private fun closeTaskMenu(): Boolean {
- val floatingView: AbstractFloatingView? = AbstractFloatingView.getTopOpenViewWithType(
- container,
- AbstractFloatingView.TYPE_TASK_MENU
- )
+ val floatingView: AbstractFloatingView? =
+ AbstractFloatingView.getTopOpenViewWithType(
+ container,
+ AbstractFloatingView.TYPE_TASK_MENU,
+ )
if (floatingView?.isOpen == true) {
floatingView.close(true)
return true
@@ -1503,7 +1566,7 @@ constructor(
recentsView.setTaskBorderEnabled(false)
}
return if (enableOverviewIconMenu() && menuContainer.iconView is IconAppChipView) {
- if (menuContainer.iconView.isExpanded) {
+ if (menuContainer.iconView.status == AppChipStatus.Expanded) {
closeTaskMenu()
} else {
menuContainer.iconView.revealAnim(/* isRevealing= */ true)
@@ -1705,7 +1768,7 @@ constructor(
fun getSizeAdjustment(fullscreenEnabled: Boolean) = if (fullscreenEnabled) nonGridScale else 1f
private fun applyScale() {
- val scale = persistentScale * dismissScale
+ val scale = persistentScale * dismissScale * Utilities.mapRange(modalness, 1f, modalScale)
scaleX = scale
scaleY = scale
updateFullscreenParams()
@@ -1752,7 +1815,7 @@ constructor(
updateFullscreenParams(thumbnailFullscreenParams)
taskContainers.forEach {
if (enableRefactorTaskThumbnail()) {
- it.taskContentView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
+ it.thumbnailView.cornerRadius = thumbnailFullscreenParams.currentCornerRadius
} else {
it.thumbnailViewDeprecated.setFullscreenParams(thumbnailFullscreenParams)
}
@@ -1767,9 +1830,13 @@ constructor(
private fun onModalnessUpdated(modalness: Float) {
isClickable = modalness == 0f
taskContainers.forEach {
- it.iconView.setModalAlpha(1 - modalness)
+ it.iconView.setModalAlpha(1f - modalness)
it.digitalWellBeingToast?.bannerOffsetPercentage = modalness
}
+ if (enableGridOnlyOverview()) {
+ modalAlpha = if (isSelectedTask) 1f else (1f - modalness)
+ applyScale()
+ }
}
fun resetPersistentViewTransforms() {
@@ -1825,9 +1892,10 @@ constructor(
private const val TAG = "TaskView"
private enum class Alpha {
- STABLE,
- ATTACH,
- SPLIT,
+ Stable,
+ Attach,
+ Split,
+ Modal,
}
private enum class SettledProgress {
diff --git a/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
index 18a533842d..2532fcf8cf 100644
--- a/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/ActiveGestureProtoLogProxy.java
@@ -544,11 +544,30 @@ public class ActiveGestureProtoLogProxy {
@NonNull Point displaySize, @NonNull RectF swipeRegion, @NonNull RectF ohmRegion,
int gesturalHeight, int largerGesturalHeight, @NonNull String reason) {
if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
- ProtoLog.d(ACTIVE_GESTURE_LOG,
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
"OrientationTouchTransformer.createRegionForDisplay: "
+ "dispRot=%d, dispSize=%s, swipeRegion=%s, ohmRegion=%s, "
+ "gesturalHeight=%d, largerGesturalHeight=%d, reason=%s",
displayRotation, displaySize.flattenToString(), swipeRegion.toShortString(),
ohmRegion.toShortString(), gesturalHeight, largerGesturalHeight, reason);
}
+
+ public static void logOnTaskAnimationManagerNotAvailable(int displayId) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "TaskAnimationManager not available for displayId=%d",
+ displayId));
+ if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG, "TaskAnimationManager not available for displayId=%d",
+ displayId);
+ }
+
+ public static void logGestureStartSwipeHandler(@NonNull String interactionHandler) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "OtherActivityInputConsumer.startTouchTrackingForWindowAnimation: "
+ + "interactionHandler=%s", interactionHandler));
+ if (!enableActiveGestureProtoLog() || !isProtoLogInitialized()) return;
+ ProtoLog.d(ACTIVE_GESTURE_LOG,
+ "OtherActivityInputConsumer.startTouchTrackingForWindowAnimation: "
+ + "interactionHandler=%s", interactionHandler);
+ }
}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt
deleted file mode 100644
index 8cc09d470b..0000000000
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/SplashHelper.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.task.thumbnail
-
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-
-object SplashHelper {
- private val BITMAP_RECT_COLORS = listOf(Color.GREEN, Color.RED, Color.BLUE, Color.CYAN)
-
- fun createSplash(): Bitmap = createBitmap(width = 20, height = 20, rectColorRotation = 1)
-
- fun createBitmap(width: Int, height: Int, rectColorRotation: Int = 0): Bitmap =
- Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
- Canvas(this).apply {
- val paint = Paint()
- paint.color = BITMAP_RECT_COLORS[rectColorRotation % 4]
- drawRect(0f, 0f, width / 2f, height / 2f, paint)
- paint.color = BITMAP_RECT_COLORS[(1 + rectColorRotation) % 4]
- drawRect(width / 2f, 0f, width.toFloat(), height / 2f, paint)
- paint.color = BITMAP_RECT_COLORS[(2 + rectColorRotation) % 4]
- drawRect(0f, height / 2f, width / 2f, height.toFloat(), paint)
- paint.color = BITMAP_RECT_COLORS[(3 + rectColorRotation) % 4]
- drawRect(width / 2f, height / 2f, width.toFloat(), height.toFloat(), paint)
- }
- }
-}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt
deleted file mode 100644
index 7b1e445e7a..0000000000
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskContentViewScreenshotTest.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.task.thumbnail
-
-import android.content.Context
-import android.graphics.Color
-import android.graphics.drawable.BitmapDrawable
-import android.platform.test.flag.junit.SetFlagsRule
-import android.view.LayoutInflater
-import com.android.launcher3.Flags
-import com.android.launcher3.R
-import com.android.launcher3.util.rule.setFlags
-import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
-import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
-import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-import platform.test.screenshot.DeviceEmulationSpec
-import platform.test.screenshot.Displays
-import platform.test.screenshot.ViewScreenshotTestRule
-import platform.test.screenshot.getEmulatedDevicePathConfig
-
-/** Screenshot tests for [TaskContentView]. */
-@RunWith(ParameterizedAndroidJunit4::class)
-class TaskContentViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
-
- @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
-
- @get:Rule(order = 1)
- val screenshotRule =
- ViewScreenshotTestRule(
- emulationSpec,
- ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
- )
-
- @Before
- fun setUp() {
- setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL)
- }
-
- @Test
- fun taskContentView_recyclesToUninitialized() {
- screenshotRule.screenshotTest("taskContentView_uninitialized") { activity ->
- activity.actionBar?.hide()
- val taskContentView = createTaskContentView(activity)
- taskContentView.setState(
- TaskHeaderUiState.HideHeader,
- BackgroundOnly(Color.YELLOW),
- null,
- )
- taskContentView.onRecycle()
- taskContentView
- }
- }
-
- @Test
- fun taskContentView_shows_thumbnail_and_header() {
- screenshotRule.screenshotTest("taskContentView_shows_thumbnail_and_header") { activity ->
- activity.actionBar?.hide()
- createTaskContentView(activity).apply {
- setState(
- TaskHeaderUiState.ShowHeader(
- TaskHeaderUiState.ThumbnailHeader(
- BitmapDrawable(activity.resources, createSplash()),
- "test",
- ) {}
- ),
- BackgroundOnly(Color.YELLOW),
- null,
- )
- }
- }
- }
-
- @Test
- fun taskContentView_scaled_roundRoundedCorners() {
- screenshotRule.screenshotTest("taskContentView_scaledRoundedCorners") { activity ->
- activity.actionBar?.hide()
- createTaskContentView(activity).apply {
- scaleX = 0.75f
- scaleY = 0.3f
- setState(TaskHeaderUiState.HideHeader, BackgroundOnly(Color.YELLOW), null)
- }
- }
- }
-
- private fun createTaskContentView(context: Context): TaskContentView {
- val taskContentView =
- LayoutInflater.from(context).inflate(R.layout.task_content_view, null, false)
- as TaskContentView
- taskContentView.cornerRadius = CORNER_RADIUS
- return taskContentView
- }
-
- companion object {
- @Parameters(name = "{0}")
- @JvmStatic
- fun getTestSpecs() =
- DeviceEmulationSpec.forDisplays(
- Displays.Phone,
- isDarkTheme = false,
- isLandscape = false,
- )
-
- const val CORNER_RADIUS = 56f
- }
-}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt
deleted file mode 100644
index e30554e647..0000000000
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskHeaderViewScreenshotTest.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2025 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.task.thumbnail
-
-import android.content.Context
-import android.graphics.drawable.BitmapDrawable
-import android.view.LayoutInflater
-import com.android.launcher3.R
-import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
-import com.android.quickstep.views.TaskHeaderView
-import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-import platform.test.screenshot.DeviceEmulationSpec
-import platform.test.screenshot.Displays
-import platform.test.screenshot.ViewScreenshotTestRule
-import platform.test.screenshot.getEmulatedDevicePathConfig
-
-/** Screenshot tests for [TaskHeaderView]. */
-@RunWith(ParameterizedAndroidJunit4::class)
-class TaskHeaderViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
- @get:Rule
- val screenshotRule =
- ViewScreenshotTestRule(
- emulationSpec,
- ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
- )
-
- @Test
- fun taskHeaderView_showHeader() {
- screenshotRule.screenshotTest("taskHeaderView_showHeader") { activity ->
- activity.actionBar?.hide()
- createTaskHeaderView(activity).apply {
- setState(
- TaskHeaderUiState.ShowHeader(
- TaskHeaderUiState.ThumbnailHeader(
- BitmapDrawable(activity.resources, createSplash()),
- "Example",
- ) {}
- )
- )
- }
- }
- }
-
- private fun createTaskHeaderView(context: Context): TaskHeaderView {
- val taskHeaderView =
- LayoutInflater.from(context).inflate(R.layout.task_header_view, null, false)
- as TaskHeaderView
- return taskHeaderView
- }
-
- companion object {
- @Parameters(name = "{0}")
- @JvmStatic
- fun getTestSpecs() =
- DeviceEmulationSpec.forDisplays(
- Displays.Tablet,
- isDarkTheme = false,
- isLandscape = true,
- )
- }
-}
diff --git a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
index 45df73572d..80b2c16e10 100644
--- a/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
+++ b/quickstep/tests/multivalentScreenshotTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewScreenshotTest.kt
@@ -16,14 +16,16 @@
package com.android.quickstep.task.thumbnail
import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
+import android.graphics.Paint
import android.graphics.drawable.BitmapDrawable
import android.view.LayoutInflater
import android.view.Surface.ROTATION_0
+import androidx.core.graphics.set
import com.android.launcher3.R
-import com.android.quickstep.task.thumbnail.SplashHelper.createBitmap
-import com.android.quickstep.task.thumbnail.SplashHelper.createSplash
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.SnapshotSplash
@@ -88,25 +90,23 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
}
@Test
- fun taskThumbnailView_liveTile() {
+ fun taskThumbnailView_liveTile_withoutHeader() {
screenshotRule.screenshotTest("taskThumbnailView_liveTile") { activity ->
activity.actionBar?.hide()
- createTaskThumbnailView(activity).apply { setState(TaskThumbnailUiState.LiveTile) }
+ createTaskThumbnailView(activity).apply {
+ setState(TaskThumbnailUiState.LiveTile.WithoutHeader)
+ }
}
}
@Test
- fun taskThumbnailView_image() {
+ fun taskThumbnailView_image_withoutHeader() {
screenshotRule.screenshotTest("taskThumbnailView_image") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
setState(
SnapshotSplash(
- Snapshot(
- createBitmap(VIEW_ENV_WIDTH, VIEW_ENV_HEIGHT),
- ROTATION_0,
- Color.DKGRAY,
- ),
+ Snapshot.WithoutHeader(createBitmap(), ROTATION_0, Color.DKGRAY),
null,
)
)
@@ -115,14 +115,14 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
}
@Test
- fun taskThumbnailView_image_withImageMatrix() {
+ fun taskThumbnailView_image_withoutHeader_withImageMatrix() {
screenshotRule.screenshotTest("taskThumbnailView_image_withMatrix") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
val lessThanHeightMatchingAspectRatio = (VIEW_ENV_HEIGHT / 2) - 200
setState(
SnapshotSplash(
- Snapshot(
+ Snapshot.WithoutHeader(
createBitmap(
width = VIEW_ENV_WIDTH / 2,
height = lessThanHeightMatchingAspectRatio,
@@ -139,17 +139,13 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
}
@Test
- fun taskThumbnailView_splash() {
+ fun taskThumbnailView_splash_withoutHeader() {
screenshotRule.screenshotTest("taskThumbnailView_partial_splash") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
setState(
SnapshotSplash(
- Snapshot(
- createBitmap(VIEW_ENV_WIDTH, VIEW_ENV_HEIGHT),
- ROTATION_0,
- Color.DKGRAY,
- ),
+ Snapshot.WithoutHeader(createBitmap(), ROTATION_0, Color.DKGRAY),
BitmapDrawable(activity.resources, createSplash()),
)
)
@@ -159,14 +155,14 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
}
@Test
- fun taskThumbnailView_splash_withImageMatrix() {
+ fun taskThumbnailView_splash_withoutHeader_withImageMatrix() {
screenshotRule.screenshotTest("taskThumbnailView_partial_splash_withMatrix") { activity ->
activity.actionBar?.hide()
createTaskThumbnailView(activity).apply {
val lessThanHeightMatchingAspectRatio = (VIEW_ENV_HEIGHT / 2) - 200
setState(
SnapshotSplash(
- Snapshot(
+ Snapshot.WithoutHeader(
createBitmap(
width = VIEW_ENV_WIDTH / 2,
height = lessThanHeightMatchingAspectRatio,
@@ -233,9 +229,31 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
val taskThumbnailView =
LayoutInflater.from(context).inflate(R.layout.task_thumbnail, null, false)
as TaskThumbnailView
+ taskThumbnailView.cornerRadius = CORNER_RADIUS
return taskThumbnailView
}
+ private fun createSplash() = createBitmap(width = 20, height = 20, rectColorRotation = 1)
+
+ private fun createBitmap(
+ width: Int = VIEW_ENV_WIDTH,
+ height: Int = VIEW_ENV_HEIGHT,
+ rectColorRotation: Int = 0,
+ ) =
+ Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {
+ Canvas(this).apply {
+ val paint = Paint()
+ paint.color = BITMAP_RECT_COLORS[rectColorRotation % 4]
+ drawRect(0f, 0f, width / 2f, height / 2f, paint)
+ paint.color = BITMAP_RECT_COLORS[(1 + rectColorRotation) % 4]
+ drawRect(width / 2f, 0f, width.toFloat(), height / 2f, paint)
+ paint.color = BITMAP_RECT_COLORS[(2 + rectColorRotation) % 4]
+ drawRect(0f, height / 2f, width / 2f, height.toFloat(), paint)
+ paint.color = BITMAP_RECT_COLORS[(3 + rectColorRotation) % 4]
+ drawRect(width / 2f, height / 2f, width.toFloat(), height.toFloat(), paint)
+ }
+ }
+
companion object {
@Parameters(name = "{0}")
@JvmStatic
@@ -246,6 +264,8 @@ class TaskThumbnailViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
isLandscape = false,
)
+ const val CORNER_RADIUS = 56f
+ val BITMAP_RECT_COLORS = listOf(Color.GREEN, Color.RED, Color.BLUE, Color.CYAN)
const val VIEW_ENV_WIDTH = 1440
const val VIEW_ENV_HEIGHT = 3120
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
index d2abed8501..42adfec82c 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/data/TaskViewItemInfoTest.kt
@@ -35,7 +35,6 @@ import com.android.launcher3.util.UserIconInfo
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
import com.android.quickstep.recents.di.RecentsDependencies
-import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.TaskContainer
@@ -199,7 +198,6 @@ class TaskViewItemInfoTest {
return TaskContainer(
taskView,
task,
- mock<TaskContentView>(),
if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
else mock<TaskThumbnailViewDeprecated>(),
mock<TaskViewIcon>(),
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
index 26f1197f7e..52d288a723 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
@@ -17,6 +17,7 @@ package com.android.launcher3.taskbar
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController
import com.android.launcher3.taskbar.bubbles.BubbleControllers
+import com.android.launcher3.taskbar.growth.NudgeController
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController
import com.android.systemui.shared.rotation.RotationButtonController
import java.util.Optional
@@ -58,6 +59,7 @@ abstract class TaskbarBaseTestCase {
@Mock lateinit var taskbarPinningController: TaskbarPinningController
@Mock lateinit var optionalBubbleControllers: Optional<BubbleControllers>
@Mock lateinit var taskbarDesktopModeController: TaskbarDesktopModeController
+ @Mock lateinit var nudgeController: NudgeController
lateinit var taskbarControllers: TaskbarControllers
@@ -100,6 +102,7 @@ abstract class TaskbarBaseTestCase {
taskbarPinningController,
optionalBubbleControllers,
taskbarDesktopModeController,
+ nudgeController,
)
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.kt
new file mode 100644
index 0000000000..d2b9fcf2c5
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar
+
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_HOVER_ENTER
+import android.view.MotionEvent.ACTION_HOVER_EXIT
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.BubbleTextView
+import com.android.launcher3.R
+import com.android.launcher3.apppairs.AppPairIcon
+import com.android.launcher3.folder.FolderIcon
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
+import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatAppPairsItem
+import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatFolderItem
+import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatWorkspaceItem
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
+import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
+import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
+class TaskbarHoverToolTipControllerTest {
+
+ @get:Rule(order = 0) val context = TaskbarWindowSandboxContext.create()
+ @get:Rule(order = 1) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+
+ @InjectController lateinit var autohideSuspendController: TaskbarAutohideSuspendController
+ @InjectController lateinit var popupController: TaskbarPopupController
+
+ private val taskbarContext: TaskbarActivityContext by taskbarUnitTestRule::activityContext
+
+ private lateinit var taskbarView: TaskbarView
+ private lateinit var iconView: BubbleTextView
+ private lateinit var appPairIcon: AppPairIcon
+ private lateinit var folderIcon: FolderIcon
+
+ private val isHoverToolTipOpen: Boolean
+ get() {
+ // TaskbarHoverToolTip uses ArrowTipView which is type TYPE_ON_BOARD_POPUP.
+ return AbstractFloatingView.hasOpenView(
+ taskbarContext,
+ AbstractFloatingView.TYPE_ON_BOARD_POPUP,
+ )
+ }
+
+ @Before
+ fun setup() {
+ runOnMainSync { taskbarView = taskbarContext.dragLayer.findViewById(R.id.taskbar_view) }
+
+ val hotseatItems =
+ arrayOf(
+ createHotseatWorkspaceItem(),
+ createHotseatAppPairsItem(),
+ createHotseatFolderItem(),
+ )
+ runOnMainSync {
+ taskbarView.updateItems(hotseatItems, emptyList())
+ iconView =
+ taskbarView.iconViews.filterIsInstance<BubbleTextView>().first {
+ it.tag is WorkspaceItemInfo
+ }
+ appPairIcon = taskbarView.iconViews.filterIsInstance<AppPairIcon>().first()
+ folderIcon = taskbarView.iconViews.filterIsInstance<FolderIcon>().first()
+ }
+ }
+
+ @Test
+ fun onHover_hoverEnterIcon_revealToolTip_hoverExitIcon_closeToolTip() {
+ runOnMainSync { iconView.dispatchGenericMotionEvent(HOVER_ENTER) }
+ assertThat(isHoverToolTipOpen).isTrue()
+ assertThat(autohideSuspendController.isTransientTaskbarStashingSuspended).isTrue()
+ runOnMainSync { iconView.dispatchGenericMotionEvent(HOVER_EXIT) }
+ assertThat(isHoverToolTipOpen).isFalse()
+ assertThat(autohideSuspendController.isTransientTaskbarStashingSuspended).isFalse()
+ }
+
+ @Test
+ fun onHover_hoverEnterFolderIcon_revealToolTip_hoverExitFolderIcon_closeToolTip() {
+ runOnMainSync { folderIcon.dispatchGenericMotionEvent(HOVER_ENTER) }
+ assertThat(isHoverToolTipOpen).isTrue()
+ assertThat(autohideSuspendController.isTransientTaskbarStashingSuspended).isTrue()
+ runOnMainSync { folderIcon.dispatchGenericMotionEvent(HOVER_EXIT) }
+ assertThat(isHoverToolTipOpen).isFalse()
+ assertThat(autohideSuspendController.isTransientTaskbarStashingSuspended).isFalse()
+ }
+
+ @Test
+ fun onHover_hoverEnterAppPair_revealToolTip_hoverExitAppPair_closeToolTip() {
+ runOnMainSync { appPairIcon.dispatchGenericMotionEvent(HOVER_ENTER) }
+ assertThat(isHoverToolTipOpen).isTrue()
+ assertThat(autohideSuspendController.isTransientTaskbarStashingSuspended).isTrue()
+ runOnMainSync { appPairIcon.dispatchGenericMotionEvent(HOVER_EXIT) }
+ assertThat(isHoverToolTipOpen).isFalse()
+ assertThat(autohideSuspendController.isTransientTaskbarStashingSuspended).isFalse()
+ }
+
+ @Test
+ fun onHover_hoverEnterIconAlignedWithHotseat_noToolTip() {
+ taskbarContext.setUIController(
+ object : TaskbarUIController() {
+ override fun isIconAlignedWithHotseat(): Boolean = true
+ }
+ )
+
+ runOnMainSync { iconView.dispatchGenericMotionEvent(HOVER_ENTER) }
+ assertThat(isHoverToolTipOpen).isFalse()
+ }
+
+ @Test
+ fun onHover_hoverEnterFolderOpen_noToolTip() {
+ runOnMainSync {
+ folderIcon.folder.animateOpen()
+ iconView.dispatchGenericMotionEvent(HOVER_ENTER)
+ }
+ assertThat(isHoverToolTipOpen).isFalse()
+ }
+
+ @Test
+ fun onHover_hoverEnterPopupOpen_noToolTip() {
+ runOnMainSync {
+ popupController.showForIcon(iconView)
+ iconView.dispatchGenericMotionEvent(HOVER_ENTER)
+ }
+ assertThat(isHoverToolTipOpen).isFalse()
+ }
+
+ companion object {
+ private val HOVER_EXIT = MotionEvent.obtain(0, 0, ACTION_HOVER_EXIT, 0f, 0f, 0)
+ private val HOVER_ENTER = MotionEvent.obtain(0, 0, ACTION_HOVER_ENTER, 0f, 0f, 0)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
index 37610444b5..c589415196 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
@@ -23,6 +23,7 @@ import android.os.Process
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
+import android.view.Display.DEFAULT_DISPLAY
import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.Flags.FLAG_ENABLE_MULTI_INSTANCE_MENU_TASKBAR
import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW
@@ -446,7 +447,7 @@ class TaskbarOverflowTest {
)
})
- recentsModel.updateRecentTasks(listOf(DesktopTask(deskId = 0, tasks)))
+ recentsModel.updateRecentTasks(listOf(DesktopTask(deskId = 0, DEFAULT_DISPLAY, tasks)))
for (task in 1..tasks.size) {
desktopTaskListener?.onTasksVisibilityChanged(
context.virtualDisplay.display.displayId,
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
index 8376bc183c..334d8ab061 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
@@ -25,6 +25,7 @@ import android.graphics.Rect
import android.os.Process
import android.os.UserHandle
import android.platform.test.annotations.EnableFlags
+import android.view.Display.DEFAULT_DISPLAY
import androidx.test.annotation.UiThreadTest
import com.android.internal.R
import com.android.launcher3.BubbleTextView.RunningAppState
@@ -877,7 +878,7 @@ class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
val allTasks =
ArrayList<GroupTask>().apply {
if (!runningTasks.isEmpty()) {
- add(DesktopTask(deskId = 0, ArrayList(runningTasks)))
+ add(DesktopTask(deskId = 0, DEFAULT_DISPLAY, ArrayList(runningTasks)))
}
addAll(recentTasks)
}
@@ -959,7 +960,7 @@ class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
private fun setInDesktopMode(inDesktopMode: Boolean) {
whenever(taskbarControllers.taskbarDesktopModeController.shouldShowDesktopTasksInTaskbar())
.thenReturn(inDesktopMode)
- whenever(taskbarControllers.taskbarDesktopModeController.isInDesktopMode)
+ whenever(taskbarControllers.taskbarDesktopModeController.isInDesktopMode(DEFAULT_DISPLAY))
.thenReturn(inDesktopMode)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
index e52aacf6d5..92abbbaa0a 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarViewTestUtil.kt
@@ -18,8 +18,13 @@ package com.android.launcher3.taskbar
import android.content.ComponentName
import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.Bitmap.createBitmap
import android.os.Process
+import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.model.data.AppInfo
+import com.android.launcher3.model.data.AppPairInfo
+import com.android.launcher3.model.data.FolderInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.taskbar.TaskbarIconType.ALL_APPS
@@ -53,7 +58,27 @@ object TaskbarViewTestUtil {
return WorkspaceItemInfo(
AppInfo(TEST_COMPONENT, "Test App $id", Process.myUserHandle(), Intent())
)
- .apply { this.id = id }
+ .apply {
+ this.id = id
+ // Create a placeholder icon so that the test doesn't try to load a high-res icon.
+ this.bitmap = BitmapInfo.fromBitmap(createBitmap(1, 1, Bitmap.Config.ALPHA_8))
+ }
+ }
+
+ fun createHotseatAppPairsItem(): AppPairInfo {
+ return AppPairInfo().apply {
+ add(createHotseatWorkspaceItem(1))
+ add(createHotseatWorkspaceItem(2))
+ }
+ }
+
+ fun createHotseatFolderItem(): FolderInfo {
+ return FolderInfo().apply {
+ title = "Test Folder"
+ add(createHotseatWorkspaceItem(1))
+ add(createHotseatWorkspaceItem(2))
+ add(createHotseatWorkspaceItem(3))
+ }
}
/** Creates a list of fake recent tasks. */
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index 2dacf69590..19c88240d9 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -28,6 +28,7 @@ import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarControllers
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks
+import com.android.launcher3.taskbar.TaskbarUIController
import com.android.launcher3.taskbar.bubbles.BubbleControllers
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
@@ -117,6 +118,8 @@ class TaskbarUnitTestRule(
super.recreateTaskbars()
if (currentActivityContext != null) {
injectControllers()
+ // TODO(b/346394875): we should test a non-default uiController.
+ activityContext.setUIController(TaskbarUIController.DEFAULT)
controllerInjectionCallback.invoke()
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
index 6fbbd597d0..568da2d889 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AbsSwipeUpHandlerTestCase.java
@@ -49,6 +49,7 @@ import android.os.SystemClock;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.Display;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.ViewTreeObserver;
@@ -100,7 +101,8 @@ public abstract class AbsSwipeUpHandlerTestCase<
protected final ActivityManager.RunningTaskInfo mRunningTaskInfo =
new ActivityManager.RunningTaskInfo();
protected final TopTaskTracker.CachedTaskInfo mCachedTaskInfo =
- new TopTaskTracker.CachedTaskInfo(Collections.singletonList(mRunningTaskInfo));
+ new TopTaskTracker.CachedTaskInfo(
+ Collections.singletonList(mRunningTaskInfo), /* canEnterDesktopMode = */ false);
protected final RemoteAnimationTarget mRemoteAnimationTarget = new RemoteAnimationTarget(
/* taskId= */ 0,
/* mode= */ RemoteAnimationTarget.MODE_CLOSING,
@@ -189,7 +191,7 @@ public abstract class AbsSwipeUpHandlerTestCase<
@Before
public void setUpRecentsContainer() {
mTaskAnimationManager = new TaskAnimationManager(mContext,
- RecentsAnimationDeviceState.INSTANCE.get(mContext));
+ RecentsAnimationDeviceState.INSTANCE.get(mContext), Display.DEFAULT_DISPLAY);
RecentsViewContainer recentsContainer = getRecentsContainer();
RECENTS_VIEW recentsView = getRecentsView();
@@ -291,7 +293,7 @@ public abstract class AbsSwipeUpHandlerTestCase<
public void testHomeGesture_invalidatesHandlerAfterParallelAnim() {
ValueAnimator parallelAnim = new ValueAnimator();
parallelAnim.setRepeatCount(ValueAnimator.INFINITE);
- when(mActivityInterface.getParallelAnimationToLauncher(any(), anyLong(), any()))
+ when(mActivityInterface.getParallelAnimationToGestureEndTarget(any(), anyLong(), any()))
.thenReturn(parallelAnim);
SWIPE_HANDLER handler = createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME);
runOnMainSync(() -> {
@@ -305,7 +307,7 @@ public abstract class AbsSwipeUpHandlerTestCase<
@Test
public void testHomeGesture_invalidatesHandlerIfNoParallelAnim() {
- when(mActivityInterface.getParallelAnimationToLauncher(any(), anyLong(), any()))
+ when(mActivityInterface.getParallelAnimationToGestureEndTarget(any(), anyLong(), any()))
.thenReturn(null);
SWIPE_HANDLER handler = createSwipeUpHandlerForGesture(GestureState.GestureEndTarget.HOME);
runOnMainSync(() -> {
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt
index 56c01f94a5..11e0ee88b6 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt
@@ -17,6 +17,7 @@
package com.android.quickstep
import android.platform.test.flag.junit.SetFlagsRule
+import android.view.Display.DEFAULT_DISPLAY
import androidx.test.filters.SmallTest
import com.android.launcher3.Flags
import com.android.launcher3.util.LauncherMultivalentJUnit
@@ -25,6 +26,7 @@ import com.android.launcher3.util.rule.setFlags
import com.android.quickstep.OverviewCommandHelper.CommandInfo
import com.android.quickstep.OverviewCommandHelper.CommandInfo.CommandStatus
import com.android.quickstep.OverviewCommandHelper.CommandType
+import com.android.quickstep.fallback.window.RecentsDisplayModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
@@ -41,9 +43,9 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.spy
-import org.mockito.Mockito.`when`
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(LauncherMultivalentJUnit::class)
@@ -57,19 +59,38 @@ class OverviewCommandHelperTest {
private var pendingCallbacksWithDelays = mutableListOf<Long>()
+ private val recentsDisplayModel: RecentsDisplayModel = mock()
+ private val defaultDisplayResource: RecentsDisplayModel.RecentsDisplayResource = mock()
+ private val secondaryDisplayResource: RecentsDisplayModel.RecentsDisplayResource = mock()
+ private val executeCommandDisplayIds = mutableListOf<Int>()
+
+ private fun setupDefaultDisplay() {
+ whenever(defaultDisplayResource.displayId).thenReturn(DEFAULT_DISPLAY)
+ whenever(recentsDisplayModel.activeDisplayResources)
+ .thenReturn(listOf(defaultDisplayResource))
+ }
+
+ private fun setupMultipleDisplays() {
+ whenever(defaultDisplayResource.displayId).thenReturn(DEFAULT_DISPLAY)
+ whenever(secondaryDisplayResource.displayId).thenReturn(1)
+ whenever(recentsDisplayModel.activeDisplayResources)
+ .thenReturn(listOf(defaultDisplayResource, secondaryDisplayResource))
+ }
+
@Suppress("UNCHECKED_CAST")
@Before
fun setup() {
setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_OVERVIEW_COMMAND_HELPER_TIMEOUT)
+ setupDefaultDisplay()
+
sut =
spy(
OverviewCommandHelper(
touchInteractionService = mock(),
overviewComponentObserver = mock(),
- taskAnimationManager = mock(),
dispatcherProvider = TestDispatcherProvider(dispatcher),
- recentsDisplayModel = mock(),
+ recentsDisplayModel = recentsDisplayModel,
focusState = mock(),
taskbarManager = mock(),
)
@@ -87,6 +108,8 @@ class OverviewCommandHelperTest {
}
}
}
+ val commandInfo = invocation.arguments[0] as CommandInfo
+ executeCommandDisplayIds.add(commandInfo.displayId)
delayInMillis == null // if no callback to execute, returns success
}
.`when`(sut)
@@ -176,7 +199,61 @@ class OverviewCommandHelperTest {
assertThat(commandInfo2.status).isEqualTo(CommandStatus.COMPLETED)
}
+ @Test
+ fun whenAllDisplaysCommandIsAdded_singleCommandProcessedForDefaultDisplay() =
+ testScope.runTest {
+ executeCommandDisplayIds.clear()
+ // Add command to queue
+ val commandInfo: CommandInfo = sut.addCommandsForAllDisplays(CommandType.HOME)!!
+ assertThat(commandInfo.status).isEqualTo(CommandStatus.IDLE)
+ runCurrent()
+ assertThat(commandInfo.status).isEqualTo(CommandStatus.COMPLETED)
+ assertThat(executeCommandDisplayIds).containsExactly(DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun whenAllDisplaysCommandIsAdded_multipleCommandsProcessedForMultipleDisplays() =
+ testScope.runTest {
+ setupMultipleDisplays()
+ executeCommandDisplayIds.clear()
+ // Add command to queue
+ val commandInfo: CommandInfo = sut.addCommandsForAllDisplays(CommandType.HOME)!!
+ assertThat(commandInfo.status).isEqualTo(CommandStatus.IDLE)
+ runCurrent()
+ assertThat(commandInfo.status).isEqualTo(CommandStatus.COMPLETED)
+ assertThat(executeCommandDisplayIds)
+ .containsExactly(DEFAULT_DISPLAY, EXTERNAL_DISPLAY_ID)
+ }
+
+ @Test
+ fun whenAllExceptDisplayCommandIsAdded_otherDisplayProcessed() =
+ testScope.runTest {
+ setupMultipleDisplays()
+ executeCommandDisplayIds.clear()
+ // Add command to queue
+ val commandInfo: CommandInfo =
+ sut.addCommandsForDisplaysExcept(CommandType.HOME, DEFAULT_DISPLAY)!!
+ assertThat(commandInfo.status).isEqualTo(CommandStatus.IDLE)
+ runCurrent()
+ assertThat(commandInfo.status).isEqualTo(CommandStatus.COMPLETED)
+ assertThat(executeCommandDisplayIds).containsExactly(EXTERNAL_DISPLAY_ID)
+ }
+
+ @Test
+ fun whenSingleDisplayCommandIsAdded_thatDisplayIsProcessed() =
+ testScope.runTest {
+ executeCommandDisplayIds.clear()
+ val displayId = 5
+ // Add command to queue
+ val commandInfo: CommandInfo = sut.addCommand(CommandType.HOME, displayId)!!
+ assertThat(commandInfo.status).isEqualTo(CommandStatus.IDLE)
+ runCurrent()
+ assertThat(commandInfo.status).isEqualTo(CommandStatus.COMPLETED)
+ assertThat(executeCommandDisplayIds).containsExactly(displayId)
+ }
+
private companion object {
const val QUEUE_TIMEOUT = 5001L
+ const val EXTERNAL_DISPLAY_ID = 1
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
index 9722e9dd99..1e4315a040 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
@@ -52,6 +52,7 @@ import com.android.internal.R;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.LooperExecutor;
+import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.TaskViewType;
import com.android.systemui.shared.recents.model.Task;
@@ -148,6 +149,66 @@ public class RecentTasksListTest {
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ public void loadTasksInBackground_freeformTask_multiDesksInMultiDisplays() throws Exception {
+ List<TaskInfo> tasksInDefaultDesk1 = Arrays.asList(
+ createRecentTaskInfo(/* taskId = */ 1, DEFAULT_DISPLAY),
+ createRecentTaskInfo(/* taskId = */ 4, DEFAULT_DISPLAY));
+ List<TaskInfo> tasksInDefaultDesk2 = Arrays.asList(
+ createRecentTaskInfo(/* taskId = */ 2, DEFAULT_DISPLAY),
+ createRecentTaskInfo(/* taskId = */ 3, DEFAULT_DISPLAY));
+ List<TaskInfo> tasksInExtend = Arrays.asList(
+ createRecentTaskInfo(/* taskId = */ 5, /* displayId = */ 1),
+ createRecentTaskInfo(/* taskId = */ 6, /* displayId = */ 1));
+ GroupedTaskInfo recentTaskInfosOfDesk1 = GroupedTaskInfo.forDeskTasks(/* deskId = */1,
+ DEFAULT_DISPLAY, tasksInDefaultDesk1, /* minimizedTaskIds = */
+ Collections.emptySet());
+ GroupedTaskInfo recentTaskInfosOfDesk2 = GroupedTaskInfo.forDeskTasks(/* deskId = */2,
+ DEFAULT_DISPLAY, tasksInDefaultDesk2, /* minimizedTaskIds = */
+ Collections.emptySet());
+ GroupedTaskInfo recentTaskInfosOfDesk3 = GroupedTaskInfo.forDeskTasks(/* deskId = */3,
+ /* displayId = */ 1, tasksInExtend, /* minimizedTaskIds = */
+ Collections.emptySet());
+ when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt())).thenReturn(
+ new ArrayList<>(Arrays.asList(recentTaskInfosOfDesk1, recentTaskInfosOfDesk2,
+ recentTaskInfosOfDesk3)));
+
+ List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
+ false);
+
+ assertThat(taskList).hasSize(3);
+ assertThat(taskList.get(2).taskViewType).isEqualTo(TaskViewType.DESKTOP);
+ List<Task> actualFreeformTasksInDesk1 = taskList.get(2).getTasks();
+ assertThat(actualFreeformTasksInDesk1).hasSize(2);
+ assertThat(actualFreeformTasksInDesk1.get(0).key.id).isEqualTo(1);
+ assertThat(actualFreeformTasksInDesk1.get(0).isMinimized).isFalse();
+ assertThat(actualFreeformTasksInDesk1.get(1).key.id).isEqualTo(4);
+ assertThat(actualFreeformTasksInDesk1.get(1).isMinimized).isFalse();
+ assertThat(((DesktopTask) taskList.get(2)).getDeskId()).isEqualTo(1);
+ assertThat(((DesktopTask) taskList.get(2)).getDisplayId()).isEqualTo(DEFAULT_DISPLAY);
+
+ assertThat(taskList.get(1).taskViewType).isEqualTo(TaskViewType.DESKTOP);
+ List<Task> actualFreeformTasksInDesk2 = taskList.get(1).getTasks();
+ assertThat(actualFreeformTasksInDesk2).hasSize(2);
+ assertThat(actualFreeformTasksInDesk2.get(0).key.id).isEqualTo(2);
+ assertThat(actualFreeformTasksInDesk2.get(0).isMinimized).isFalse();
+ assertThat(actualFreeformTasksInDesk2.get(1).key.id).isEqualTo(3);
+ assertThat(actualFreeformTasksInDesk2.get(1).isMinimized).isFalse();
+ assertThat(((DesktopTask) taskList.get(1)).getDeskId()).isEqualTo(2);
+ assertThat(((DesktopTask) taskList.get(1)).getDisplayId()).isEqualTo(DEFAULT_DISPLAY);
+
+ assertThat(taskList.get(0).taskViewType).isEqualTo(TaskViewType.DESKTOP);
+ List<Task> actualFreeformTasksInDesk3 = taskList.get(0).getTasks();
+ assertThat(actualFreeformTasksInDesk3).hasSize(2);
+ assertThat(actualFreeformTasksInDesk3.get(0).key.id).isEqualTo(5);
+ assertThat(actualFreeformTasksInDesk3.get(0).isMinimized).isFalse();
+ assertThat(actualFreeformTasksInDesk3.get(1).key.id).isEqualTo(6);
+ assertThat(actualFreeformTasksInDesk3.get(1).isMinimized).isFalse();
+ assertThat(((DesktopTask) taskList.get(0)).getDeskId()).isEqualTo(3);
+ assertThat(((DesktopTask) taskList.get(0)).getDisplayId()).isEqualTo(1);
+ }
+
+ @Test
public void loadTasksInBackground_moreThanKeys_hasValidTaskDescription() throws Exception {
String taskDescription = "Wheeee!";
RecentTaskInfo task1 = new RecentTaskInfo();
@@ -175,7 +236,8 @@ public class RecentTasksListTest {
}
@Test
- @DisableFlags(FLAG_ENABLE_SEPARATE_EXTERNAL_DISPLAY_TASKS)
+ @DisableFlags({FLAG_ENABLE_SEPARATE_EXTERNAL_DISPLAY_TASKS,
+ FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND})
public void loadTasksInBackground_freeformTask_createsDesktopTask() throws Exception {
List<TaskInfo> tasks = Arrays.asList(
createRecentTaskInfo(1 /* taskId */, DEFAULT_DISPLAY),
@@ -183,7 +245,8 @@ public class RecentTasksListTest {
createRecentTaskInfo(5 /* taskId */, 1 /* displayId */),
createRecentTaskInfo(6 /* taskId */, 1 /* displayId */));
GroupedTaskInfo recentTaskInfos = GroupedTaskInfo.forDeskTasks(
- 0 /* deskId */, tasks, Collections.emptySet() /* minimizedTaskIds */);
+ 0 /* deskId */, DEFAULT_DISPLAY, tasks,
+ Collections.emptySet() /* minimizedTaskIds */);
when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
@@ -214,7 +277,8 @@ public class RecentTasksListTest {
createRecentTaskInfo(5 /* taskId */, 1 /* displayId */),
createRecentTaskInfo(6 /* taskId */, 1 /* displayId */));
GroupedTaskInfo recentTaskInfos = GroupedTaskInfo.forDeskTasks(
- 0 /* deskId */, tasks, Collections.emptySet() /* minimizedTaskIds */);
+ 0 /* deskId */, DEFAULT_DISPLAY, tasks,
+ Collections.emptySet() /* minimizedTaskIds */);
when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
@@ -248,7 +312,7 @@ public class RecentTasksListTest {
Set<Integer> minimizedTaskIds =
Arrays.stream(new Integer[]{1, 4, 5}).collect(Collectors.toSet());
GroupedTaskInfo recentTaskInfos = GroupedTaskInfo.forDeskTasks(
- 0 /* deskId */, tasks, minimizedTaskIds);
+ 0 /* deskId */, DEFAULT_DISPLAY, tasks, minimizedTaskIds);
when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/TaskAnimationManagerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/TaskAnimationManagerTest.java
index 6e9885ad43..fd88a5cb27 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/TaskAnimationManagerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/TaskAnimationManagerTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
+import android.view.Display;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -54,7 +55,7 @@ public class TaskAnimationManagerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mTaskAnimationManager = new TaskAnimationManager(mContext,
- RecentsAnimationDeviceState.INSTANCE.get(mContext)) {
+ RecentsAnimationDeviceState.INSTANCE.get(mContext), Display.DEFAULT_DISPLAY) {
@Override
SystemUiProxy getSystemUiProxy() {
return mSystemUiProxy;
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/fallback/RecentsStateUtilsTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/fallback/RecentsStateUtilsTest.kt
new file mode 100644
index 0000000000..4e9dae8da0
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/fallback/RecentsStateUtilsTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.fallback
+
+import com.android.launcher3.testing.shared.TestProtocol.BACKGROUND_APP_STATE_ORDINAL
+import com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL
+import com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL
+import com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL
+import com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL
+import com.android.launcher3.util.LauncherMultivalentJUnit
+import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+@EmulatedDevices(["pixelTablet2023"])
+class RecentsStateUtilsTest {
+
+ @Test
+ fun testRecentsStateDefault_toLauncherStateOrdinal_isOverviewStateOrdinal() {
+ assertThat(RecentsState.DEFAULT.toLauncherStateOrdinal()).isEqualTo(OVERVIEW_STATE_ORDINAL)
+ }
+
+ @Test
+ fun testRecentsStateModal_toLauncherStateOrdinal_isModalTaskStateOrdinal() {
+ assertThat(RecentsState.MODAL_TASK.toLauncherStateOrdinal())
+ .isEqualTo(OVERVIEW_MODAL_TASK_STATE_ORDINAL)
+ }
+
+ @Test
+ fun testRecentsStateBackgroundApp_toLauncherStateOrdinal_isBackgroundAppStateOrdinal() {
+ assertThat(RecentsState.BACKGROUND_APP.toLauncherStateOrdinal())
+ .isEqualTo(BACKGROUND_APP_STATE_ORDINAL)
+ }
+
+ @Test
+ fun testRecentsStateHome_toLauncherStateOrdinal_isNormalStateOrdinal() {
+ assertThat(RecentsState.HOME.toLauncherStateOrdinal()).isEqualTo(NORMAL_STATE_ORDINAL)
+ }
+
+ @Test
+ fun testRecentsStateBgLauncher_toLauncherStateOrdinal_isNormalStateOrdinal() {
+ assertThat(RecentsState.BG_LAUNCHER.toLauncherStateOrdinal())
+ .isEqualTo(NORMAL_STATE_ORDINAL)
+ }
+
+ @Test
+ fun testRecentsStateOverviewSplitSelect_toLauncherStateOrdinal_isOverviewSplitSelectStateOrdinal() {
+ assertThat(RecentsState.OVERVIEW_SPLIT_SELECT.toLauncherStateOrdinal())
+ .isEqualTo(OVERVIEW_SPLIT_SELECT_ORDINAL)
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
index cfeade8ff3..ee9505cdbb 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
@@ -36,6 +36,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -105,11 +106,12 @@ public class NavHandleLongPressInputConsumerTest {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- when(mTopTaskTracker.getCachedTopTask(anyBoolean())).thenReturn(mTaskInfo);
+ when(mTopTaskTracker.getCachedTopTask(anyBoolean(), anyInt())).thenReturn(mTaskInfo);
when(mDeviceState.getSquaredTouchSlop()).thenReturn(SQUARED_TOUCH_SLOP);
when(mDelegate.allowInterceptByParent()).thenReturn(true);
mLongPressTriggered.set(false);
- when(mNavHandleLongPressHandler.getLongPressRunnable(any())).thenReturn(mLongPressRunnable);
+ when(mNavHandleLongPressHandler.getLongPressRunnable(any(), anyInt())).thenReturn(
+ mLongPressRunnable);
when(mStatsLogger.withPackageName(any())).thenReturn(mStatsLogger);
when(mStatsLatencyLogger.withInstanceId(any())).thenReturn(mStatsLatencyLogger);
when(mStatsLatencyLogger.withLatency(anyLong())).thenReturn(mStatsLatencyLogger);
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/LandscapePagedViewHandlerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/LandscapePagedViewHandlerTest.kt
index 0570c26eed..66b3b047d7 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/LandscapePagedViewHandlerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/LandscapePagedViewHandlerTest.kt
@@ -43,12 +43,12 @@ class LandscapePagedViewHandlerTest {
if (isEnabled) {
setFlagsRule.enableFlags(
Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW,
- Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU
+ Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU,
)
} else {
setFlagsRule.disableFlags(
Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW,
- Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU
+ Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU,
)
}
}
@@ -108,14 +108,8 @@ class LandscapePagedViewHandlerTest {
val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = true)
- // TODO(b/326377497): When started in fake seascape and rotated to landscape,
- // the icon chips are in RTL and wrongly positioned at the right side of the snapshot.
- // Top-Left app chip should be placed at the top left of the first snapshot, but because
- // this issue, it's displayed at the top-right of the second snapshot.
- // The Bottom-Right app chip is displayed at the top-right of the first snapshot because
- // of this issue.
- assertThat(topLeftY).isEqualTo(0)
- assertThat(bottomRightY).isEqualTo(-316)
+ assertThat(topLeftY).isEqualTo(-316)
+ assertThat(bottomRightY).isEqualTo(0)
}
/** Test updateSplitIconsPosition */
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt
index 37886888ea..d455b0d1e9 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt
@@ -43,12 +43,12 @@ class SeascapePagedViewHandlerTest {
if (isEnabled) {
setFlagsRule.enableFlags(
Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW,
- Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU
+ Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU,
)
} else {
setFlagsRule.disableFlags(
Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW,
- Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU
+ Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU,
)
}
}
@@ -110,12 +110,6 @@ class SeascapePagedViewHandlerTest {
val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = true)
- // TODO(b/326377497): When started in fake seascape and rotated to landscape,
- // the icon chips are in RTL and wrongly positioned at the right side of the snapshot.
- // Top-Left app chip should be placed at the top left of the first snapshot, but because
- // this issue, it's displayed at the top-right of the second snapshot.
- // The Bottom-Right app chip is displayed at the top-right of the first snapshot because
- // of this issue.
assertThat(topLeftY).isEqualTo(316)
assertThat(bottomRightY).isEqualTo(0)
}
@@ -167,7 +161,7 @@ class SeascapePagedViewHandlerTest {
`when`(iconView.layoutParams).thenReturn(frameLayout)
sut.updateSplitIconsPosition(iconView, expectedTranslationY, false)
- assertThat(frameLayout.gravity).isEqualTo(Gravity.BOTTOM or Gravity.START)
+ assertThat(frameLayout.gravity).isEqualTo(Gravity.BOTTOM or Gravity.END)
verify(iconView).setSplitTranslationX(0f)
verify(iconView).setSplitTranslationY(expectedTranslationY.toFloat())
}
@@ -182,7 +176,7 @@ class SeascapePagedViewHandlerTest {
`when`(iconView.layoutParams).thenReturn(frameLayout)
sut.updateSplitIconsPosition(iconView, expectedTranslationY, true)
- assertThat(frameLayout.gravity).isEqualTo(Gravity.TOP or Gravity.END)
+ assertThat(frameLayout.gravity).isEqualTo(Gravity.TOP or Gravity.START)
verify(iconView).setSplitTranslationX(0f)
verify(iconView).setSplitTranslationY(expectedTranslationY.toFloat())
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
index 6790567a85..e22892c9cc 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -21,6 +21,7 @@ import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Rect
import android.graphics.drawable.Drawable
+import android.view.Display.DEFAULT_DISPLAY
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TestDispatcherProvider
@@ -64,7 +65,7 @@ class TasksRepositoryTest {
/* snapPosition = */ SNAP_TO_2_50_50,
),
),
- DesktopTask(deskId = 0, tasks.subList(3, 6)),
+ DesktopTask(deskId = 0, DEFAULT_DISPLAY, tasks.subList(3, 6)),
)
private val recentsModel = FakeRecentTasksDataSource()
private val taskThumbnailDataSource = FakeTaskThumbnailDataSource()
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/domain/usecase/GetThumbnailPositionUseCaseTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/domain/usecase/GetThumbnailPositionUseCaseTest.kt
index a2532809cd..7646e69117 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/domain/usecase/GetThumbnailPositionUseCaseTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/domain/usecase/GetThumbnailPositionUseCaseTest.kt
@@ -25,11 +25,14 @@ import com.android.quickstep.recents.data.FakeRecentsDeviceProfileRepository
import com.android.quickstep.recents.data.FakeRecentsRotationStateRepository
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper.PreviewPositionHelperFactory
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -38,15 +41,21 @@ import org.mockito.kotlin.whenever
class GetThumbnailPositionUseCaseTest {
private val deviceProfileRepository = FakeRecentsDeviceProfileRepository()
private val rotationStateRepository = FakeRecentsRotationStateRepository()
+ private val previewPositionHelperFactoryMock = mock<PreviewPositionHelperFactory>()
private val previewPositionHelper = mock<PreviewPositionHelper>()
private val systemUnderTest =
GetThumbnailPositionUseCase(
- deviceProfileRepository,
- rotationStateRepository,
- previewPositionHelper,
+ deviceProfileRepository = deviceProfileRepository,
+ rotationStateRepository = rotationStateRepository,
+ previewPositionHelperFactory = previewPositionHelperFactoryMock,
)
+ @Before
+ fun setUp() {
+ whenever(previewPositionHelperFactoryMock.create()).thenReturn(previewPositionHelper)
+ }
+
@Test
fun nullThumbnailData_returnsIdentityMatrix() = runTest {
val expectedResult = ThumbnailPosition(Matrix.IDENTITY_MATRIX, false)
@@ -96,6 +105,25 @@ class GetThumbnailPositionUseCaseTest {
)
}
+ @Test
+ fun multipleInvocations_usesPreviewPositionHelperFactoryEachTime() = runTest {
+ whenever(previewPositionHelper.matrix).thenReturn(MATRIX)
+
+ val sut =
+ GetThumbnailPositionUseCase(
+ deviceProfileRepository = deviceProfileRepository,
+ rotationStateRepository = rotationStateRepository,
+ previewPositionHelperFactory = previewPositionHelperFactoryMock,
+ )
+ verify(previewPositionHelperFactoryMock, times(0)).create()
+
+ sut.invoke(THUMBNAIL_DATA, CANVAS_WIDTH, CANVAS_HEIGHT, /* isRtl= */ true)
+ sut.invoke(THUMBNAIL_DATA, CANVAS_WIDTH, CANVAS_HEIGHT, /* isRtl= */ false)
+
+ // Each invocation of use case should use a fresh position helper acquired by the factory.
+ verify(previewPositionHelperFactoryMock, times(2)).create()
+ }
+
private companion object {
const val THUMBNAIL_WIDTH = 100
const val THUMBNAIL_HEIGHT = 200
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
index 6c0d0ed9ea..7ca194afcf 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
@@ -19,67 +19,55 @@ package com.android.quickstep.recents.ui.mapper
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.ShapeDrawable
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.view.Surface
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.Flags
import com.android.quickstep.recents.ui.viewmodel.TaskData
-import com.android.quickstep.task.thumbnail.TaskHeaderUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.ThumbnailHeader
import com.android.systemui.shared.recents.model.ThumbnailData
import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TaskUiStateMapperTest {
- @get:Rule val mSetFlagsRule = SetFlagsRule()
-
- /** TaskHeaderUiState */
@Test
- fun taskData_isNull_returns_HideHeader() {
+ fun taskData_isNull_returns_Uninitialized() {
val result =
- TaskUiStateMapper.toTaskHeaderState(
+ TaskUiStateMapper.toTaskThumbnailUiState(
taskData = null,
+ isLiveTile = false,
hasHeader = false,
clickCloseListener = null,
)
- assertThat(result).isEqualTo(TaskHeaderUiState.HideHeader)
+ assertThat(result).isEqualTo(TaskThumbnailUiState.Uninitialized)
}
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
- fun explodedFlagDisabled_returnsHideHeader() {
+ fun taskData_isLiveTile_returns_LiveTile() {
val inputs =
- listOf(
- TASK_DATA,
- TASK_DATA.copy(thumbnailData = null),
- TASK_DATA.copy(isLocked = true),
- TASK_DATA.copy(title = null),
- )
- val closeCallback = View.OnClickListener {}
- val expected = TaskHeaderUiState.HideHeader
- inputs.forEach { taskData ->
+ listOf(TASK_DATA, TASK_DATA.copy(thumbnailData = null), TASK_DATA.copy(isLocked = true))
+ inputs.forEach { input ->
val result =
- TaskUiStateMapper.toTaskHeaderState(
- taskData = taskData,
- hasHeader = true,
- clickCloseListener = closeCallback,
+ TaskUiStateMapper.toTaskThumbnailUiState(
+ taskData = input,
+ isLiveTile = true,
+ hasHeader = false,
+ clickCloseListener = null,
)
- assertThat(result).isEqualTo(expected)
+ assertThat(result).isEqualTo(LiveTile.WithoutHeader)
}
}
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
- fun taskData_hasHeader_and_taskData_returnsShowHeader() {
+ fun taskData_isLiveTileWithHeader_returns_LiveTileWithHeader() {
val inputs =
listOf(
TASK_DATA,
@@ -89,18 +77,14 @@ class TaskUiStateMapperTest {
)
val closeCallback = View.OnClickListener {}
val expected =
- TaskHeaderUiState.ShowHeader(
- header =
- TaskHeaderUiState.ThumbnailHeader(
- TASK_ICON,
- TASK_TITLE_DESCRIPTION,
- closeCallback,
- )
+ LiveTile.WithHeader(
+ header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION, closeCallback)
)
inputs.forEach { taskData ->
val result =
- TaskUiStateMapper.toTaskHeaderState(
+ TaskUiStateMapper.toTaskThumbnailUiState(
taskData = taskData,
+ isLiveTile = true,
hasHeader = true,
clickCloseListener = closeCallback,
)
@@ -110,7 +94,7 @@ class TaskUiStateMapperTest {
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
@Test
- fun taskData_hasHeader_emptyTaskData_returns_HideHeader() {
+ fun taskData_isLiveTileWithHeader_missingHeaderData_returns_LiveTileWithoutHeader() {
val inputs =
listOf(
TASK_DATA.copy(icon = null),
@@ -120,42 +104,30 @@ class TaskUiStateMapperTest {
inputs.forEach { taskData ->
val result =
- TaskUiStateMapper.toTaskHeaderState(
+ TaskUiStateMapper.toTaskThumbnailUiState(
taskData = taskData,
+ isLiveTile = true,
hasHeader = true,
clickCloseListener = {},
)
- assertThat(result).isEqualTo(TaskHeaderUiState.HideHeader)
- }
- }
-
- /** TaskThumbnailUiState */
- @Test
- fun taskData_isNull_returns_Uninitialized() {
- val result = TaskUiStateMapper.toTaskThumbnailUiState(taskData = null, isLiveTile = false)
- assertThat(result).isEqualTo(TaskThumbnailUiState.Uninitialized)
- }
-
- @Test
- fun taskData_isLiveTile_returns_LiveTile() {
- val inputs =
- listOf(TASK_DATA, TASK_DATA.copy(thumbnailData = null), TASK_DATA.copy(isLocked = true))
- inputs.forEach { input ->
- val result =
- TaskUiStateMapper.toTaskThumbnailUiState(taskData = input, isLiveTile = true)
- assertThat(result).isEqualTo(LiveTile)
+ assertThat(result).isEqualTo(LiveTile.WithoutHeader)
}
}
@Test
fun taskData_isStaticTile_returns_SnapshotSplash() {
val result =
- TaskUiStateMapper.toTaskThumbnailUiState(taskData = TASK_DATA, isLiveTile = false)
+ TaskUiStateMapper.toTaskThumbnailUiState(
+ taskData = TASK_DATA,
+ isLiveTile = false,
+ hasHeader = false,
+ clickCloseListener = null,
+ )
val expected =
TaskThumbnailUiState.SnapshotSplash(
snapshot =
- Snapshot(
+ Snapshot.WithoutHeader(
backgroundColor = TASK_BACKGROUND_COLOR,
bitmap = TASK_THUMBNAIL,
thumbnailRotation = Surface.ROTATION_0,
@@ -166,12 +138,72 @@ class TaskUiStateMapperTest {
assertThat(result).isEqualTo(expected)
}
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
+ @Test
+ fun taskData_isStaticTile_withHeader_returns_SnapshotSplashWithHeader() {
+ val inputs = listOf(TASK_DATA, TASK_DATA.copy(title = null))
+ val closeCallback = View.OnClickListener {}
+ val expected =
+ TaskThumbnailUiState.SnapshotSplash(
+ snapshot =
+ Snapshot.WithHeader(
+ backgroundColor = TASK_BACKGROUND_COLOR,
+ bitmap = TASK_THUMBNAIL,
+ thumbnailRotation = Surface.ROTATION_0,
+ header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION, closeCallback),
+ ),
+ splash = TASK_ICON,
+ )
+ inputs.forEach { taskData ->
+ val result =
+ TaskUiStateMapper.toTaskThumbnailUiState(
+ taskData = taskData,
+ isLiveTile = false,
+ hasHeader = true,
+ clickCloseListener = closeCallback,
+ )
+ assertThat(result).isEqualTo(expected)
+ }
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_EXPLODED_VIEW)
+ @Test
+ fun taskData_isStaticTile_missingHeaderData_returns_SnapshotSplashWithoutHeader() {
+ val inputs =
+ listOf(
+ TASK_DATA.copy(titleDescription = null, icon = null),
+ TASK_DATA.copy(titleDescription = null),
+ TASK_DATA.copy(icon = null),
+ )
+ val expected =
+ Snapshot.WithoutHeader(
+ backgroundColor = TASK_BACKGROUND_COLOR,
+ thumbnailRotation = Surface.ROTATION_0,
+ bitmap = TASK_THUMBNAIL,
+ )
+ inputs.forEach { taskData ->
+ val result =
+ TaskUiStateMapper.toTaskThumbnailUiState(
+ taskData = taskData,
+ isLiveTile = false,
+ hasHeader = true,
+ clickCloseListener = {},
+ )
+
+ assertThat(result).isInstanceOf(TaskThumbnailUiState.SnapshotSplash::class.java)
+ result as TaskThumbnailUiState.SnapshotSplash
+ assertThat(result.snapshot).isEqualTo(expected)
+ }
+ }
+
@Test
fun taskData_thumbnailIsNull_returns_BackgroundOnly() {
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = TASK_DATA.copy(thumbnailData = null),
isLiveTile = false,
+ hasHeader = false,
+ clickCloseListener = null,
)
val expected = TaskThumbnailUiState.BackgroundOnly(TASK_BACKGROUND_COLOR)
@@ -184,6 +216,8 @@ class TaskUiStateMapperTest {
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = TASK_DATA.copy(isLocked = true),
isLiveTile = false,
+ hasHeader = false,
+ clickCloseListener = null,
)
val expected = TaskThumbnailUiState.BackgroundOnly(TASK_BACKGROUND_COLOR)
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt
index 18b9fe9685..333c2856e6 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/viewmodel/TaskViewModelTest.kt
@@ -85,6 +85,7 @@ class TaskViewModelTest {
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
taskOverlayEnabled = false,
+ isCentralTask = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -132,6 +133,7 @@ class TaskViewModelTest {
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
taskOverlayEnabled = false,
+ isCentralTask = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -155,6 +157,7 @@ class TaskViewModelTest {
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
taskOverlayEnabled = false,
+ isCentralTask = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -178,6 +181,7 @@ class TaskViewModelTest {
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
taskOverlayEnabled = false,
+ isCentralTask = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -200,6 +204,7 @@ class TaskViewModelTest {
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
taskOverlayEnabled = false,
+ isCentralTask = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -218,6 +223,7 @@ class TaskViewModelTest {
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_LIGHT_THEME,
taskOverlayEnabled = false,
+ isCentralTask = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -233,6 +239,7 @@ class TaskViewModelTest {
hasHeader = false,
sysUiStatusNavFlags = FLAGS_APPEARANCE_DEFAULT,
taskOverlayEnabled = false,
+ isCentralTask = false,
)
assertThat(sut.state.first()).isEqualTo(expectedResult)
}
@@ -248,45 +255,41 @@ class TaskViewModelTest {
}
@Test
- fun taskOverlayDisabled_when_usingGroupedTask() =
+ fun taskOverlayDisabled_when_OverlayIsEnabledForInvisibleTask() =
testScope.runTest {
- sut = createTaskViewModel(TaskViewType.GROUPED)
sut.bind(TASK_MODEL_1.id)
recentsViewData.overlayEnabled.value = true
- recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
+ recentsViewData.settledFullyVisibleTaskIds.value = setOf(2)
assertThat(sut.state.first().taskOverlayEnabled).isFalse()
}
@Test
- fun taskOverlayDisabled_when_usingDesktopTask() =
+ fun taskOverlayDisabled_when_OverlayIsDisabledForVisibleTask() =
testScope.runTest {
- sut = createTaskViewModel(TaskViewType.DESKTOP)
sut.bind(TASK_MODEL_1.id)
- recentsViewData.overlayEnabled.value = true
+ recentsViewData.overlayEnabled.value = false
recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
assertThat(sut.state.first().taskOverlayEnabled).isFalse()
}
@Test
- fun taskOverlayDisabled_when_OverlayIsEnabledForInvisibleTask() =
+ fun isCentralTask_when_CentralTaskIdsMatchTaskIds() =
testScope.runTest {
- sut.bind(TASK_MODEL_1.id)
- recentsViewData.overlayEnabled.value = true
- recentsViewData.settledFullyVisibleTaskIds.value = setOf(2)
+ sut.bind(TASK_MODEL_1.id, TASK_MODEL_2.id)
+ recentsViewData.centralTaskIds.value = setOf(TASK_MODEL_1.id, TASK_MODEL_2.id)
- assertThat(sut.state.first().taskOverlayEnabled).isFalse()
+ assertThat(sut.state.first().isCentralTask).isTrue()
}
@Test
- fun taskOverlayDisabled_when_OverlayIsDisabledForVisibleTask() =
+ fun isNotCentralTask_when_CentralTaskIdsDoMatchTaskIds() =
testScope.runTest {
- sut.bind(TASK_MODEL_1.id)
- recentsViewData.overlayEnabled.value = false
- recentsViewData.settledFullyVisibleTaskIds.value = setOf(1)
+ sut.bind(TASK_MODEL_1.id, TASK_MODEL_2.id)
+ recentsViewData.centralTaskIds.value = setOf(TASK_MODEL_3.id)
- assertThat(sut.state.first().taskOverlayEnabled).isFalse()
+ assertThat(sut.state.first().isCentralTask).isFalse()
}
@Test
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/AppPairsControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/AppPairsControllerTest.kt
index 76d36d3658..c325af4d3e 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/AppPairsControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/AppPairsControllerTest.kt
@@ -17,8 +17,9 @@
package com.android.quickstep.util
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
-import android.content.Context
import android.content.res.Resources
+import android.view.Display
+import android.view.Display.DEFAULT_DISPLAY
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.apppairs.AppPairIcon
import com.android.launcher3.logging.StatsLogManager
@@ -26,6 +27,7 @@ import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
+import com.android.launcher3.views.ActivityContext
import com.android.quickstep.TopTaskTracker
import com.android.quickstep.TopTaskTracker.CachedTaskInfo
import com.android.systemui.shared.recents.model.Task
@@ -56,7 +58,7 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class AppPairsControllerTest {
- @Mock lateinit var context: Context
+ @Mock lateinit var context: ActivityContext
@Mock lateinit var resources: Resources
@Mock lateinit var splitSelectStateController: SplitSelectStateController
@Mock lateinit var statsLogManager: StatsLogManager
@@ -83,6 +85,7 @@ class AppPairsControllerTest {
}
@Mock lateinit var mockAppPairIcon: AppPairIcon
+ @Mock lateinit var mockDisplay: Display
@Mock lateinit var mockTaskbarActivityContext: TaskbarActivityContext
@Mock lateinit var mockTopTaskTracker: TopTaskTracker
@Mock lateinit var mockCachedTaskInfo: CachedTaskInfo
@@ -105,8 +108,10 @@ class AppPairsControllerTest {
// Stub methods on appPairsController so that they return mocks
spyAppPairsController = spy(appPairsController)
whenever(mockAppPairIcon.context).thenReturn(mockTaskbarActivityContext)
+ whenever(mockAppPairIcon.display).thenReturn(mockDisplay)
+ whenever(mockDisplay.displayId).thenReturn(DEFAULT_DISPLAY)
doReturn(mockTopTaskTracker).whenever(spyAppPairsController).topTaskTracker
- whenever(mockTopTaskTracker.getCachedTopTask(any())).thenReturn(mockCachedTaskInfo)
+ whenever(mockTopTaskTracker.getCachedTopTask(any(), any())).thenReturn(mockCachedTaskInfo)
whenever(mockTask1.getKey()).thenReturn(mockTaskKey1)
whenever(mockTask2.getKey()).thenReturn(mockTaskKey2)
doNothing().whenever(spyAppPairsController).launchAppPair(any(), any())
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/DesktopTaskTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/DesktopTaskTest.kt
index 6fbf4823b7..15da4d4def 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/DesktopTaskTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/DesktopTaskTest.kt
@@ -18,6 +18,7 @@ package com.android.quickstep.util
import android.content.ComponentName
import android.content.Intent
+import android.view.Display.DEFAULT_DISPLAY
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.systemui.shared.recents.model.Task
import com.google.common.truth.Truth.assertThat
@@ -29,42 +30,42 @@ class DesktopTaskTest {
@Test
fun testDesktopTask_sameInstance_isEqual() {
- val task = DesktopTask(deskId = 0, createTasks(1))
+ val task = DesktopTask(deskId = 0, DEFAULT_DISPLAY, createTasks(1))
assertThat(task).isEqualTo(task)
}
@Test
fun testDesktopTask_identicalConstructor_isEqual() {
- val task1 = DesktopTask(deskId = 0, createTasks(1))
- val task2 = DesktopTask(deskId = 0, createTasks(1))
+ val task1 = DesktopTask(deskId = 0, DEFAULT_DISPLAY, createTasks(1))
+ val task2 = DesktopTask(deskId = 0, DEFAULT_DISPLAY, createTasks(1))
assertThat(task1).isEqualTo(task2)
}
@Test
fun testDesktopTask_copy_isEqual() {
- val task1 = DesktopTask(deskId = 0, createTasks(1))
+ val task1 = DesktopTask(deskId = 0, DEFAULT_DISPLAY, createTasks(1))
val task2 = task1.copy()
assertThat(task1).isEqualTo(task2)
}
@Test
fun testDesktopTask_differentDeskIds_isNotEqual() {
- val task1 = DesktopTask(deskId = 0, createTasks(1))
- val task2 = DesktopTask(deskId = 1, createTasks(1))
+ val task1 = DesktopTask(deskId = 0, DEFAULT_DISPLAY, createTasks(1))
+ val task2 = DesktopTask(deskId = 1, DEFAULT_DISPLAY, createTasks(1))
assertThat(task1).isNotEqualTo(task2)
}
@Test
fun testDesktopTask_differentTaskIds_isNotEqual() {
- val task1 = DesktopTask(deskId = 0, createTasks(1))
- val task2 = DesktopTask(deskId = 0, createTasks(2))
+ val task1 = DesktopTask(deskId = 0, DEFAULT_DISPLAY, createTasks(1))
+ val task2 = DesktopTask(deskId = 0, DEFAULT_DISPLAY, createTasks(2))
assertThat(task1).isNotEqualTo(task2)
}
@Test
fun testDesktopTask_differentLength_isNotEqual() {
- val task1 = DesktopTask(deskId = 0, createTasks(1))
- val task2 = DesktopTask(deskId = 0, createTasks(1, 2))
+ val task1 = DesktopTask(deskId = 0, DEFAULT_DISPLAY, createTasks(1))
+ val task2 = DesktopTask(deskId = 0, DEFAULT_DISPLAY, createTasks(1, 2))
assertThat(task1).isNotEqualTo(task2)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt
index 67fc62f772..9f491717c4 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt
@@ -19,6 +19,7 @@ package com.android.quickstep.util
import android.content.ComponentName
import android.content.Intent
import android.graphics.Rect
+import android.view.Display.DEFAULT_DISPLAY
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.systemui.shared.recents.model.Task
@@ -98,7 +99,7 @@ class GroupTaskTest {
@Test
fun testGroupTask_differentType_isNotEqual() {
val task1 = SingleTask(createTask(1))
- val task2 = DesktopTask(deskId = 0, listOf(createTask(1)))
+ val task2 = DesktopTask(deskId = 0, DEFAULT_DISPLAY, listOf(createTask(1)))
assertThat(task1).isNotEqualTo(task2)
}
diff --git a/quickstep/tests/src/com/android/launcher3/statehandlers/DepthControllerTest.kt b/quickstep/tests/src/com/android/launcher3/statehandlers/DepthControllerTest.kt
new file mode 100644
index 0000000000..17cca0b7d9
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/statehandlers/DepthControllerTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.statehandlers
+
+import android.content.res.Resources
+import android.view.ViewTreeObserver
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.Launcher
+import com.android.launcher3.R
+import com.android.launcher3.dragndrop.DragLayer
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.same
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DepthControllerTest {
+
+ private lateinit var underTest: DepthController
+ @Mock private lateinit var launcher: Launcher
+ @Mock private lateinit var resource: Resources
+ @Mock private lateinit var dragLayer: DragLayer
+ @Mock private lateinit var viewTreeObserver: ViewTreeObserver
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ `when`(launcher.resources).thenReturn(resource)
+ `when`(resource.getInteger(R.integer.max_depth_blur_radius)).thenReturn(30)
+ `when`(launcher.dragLayer).thenReturn(dragLayer)
+ `when`(dragLayer.viewTreeObserver).thenReturn(viewTreeObserver)
+
+ underTest = DepthController(launcher)
+ }
+
+ @Test
+ fun setActivityStarted_add_onDrawListener() {
+ underTest.setActivityStarted(true)
+
+ verify(viewTreeObserver).addOnDrawListener(same(underTest.mOnDrawListener))
+ }
+
+ @Test
+ fun setActivityStopped_not_remove_onDrawListener() {
+ underTest.setActivityStarted(false)
+
+ // Because underTest.mOnDrawListener is never added
+ verifyNoMoreInteractions(viewTreeObserver)
+ }
+
+ @Test
+ fun setActivityStared_then_stopped_remove_onDrawListener() {
+ underTest.setActivityStarted(true)
+ reset(viewTreeObserver)
+
+ underTest.setActivityStarted(false)
+
+ verify(viewTreeObserver).removeOnDrawListener(same(underTest.mOnDrawListener))
+ }
+
+ @Test
+ fun setActivityStared_then_stopped_multiple_times_remove_onDrawListener_once() {
+ underTest.setActivityStarted(true)
+ reset(viewTreeObserver)
+
+ underTest.setActivityStarted(false)
+ underTest.setActivityStarted(false)
+ underTest.setActivityStarted(false)
+
+ // Should just remove mOnDrawListener once
+ verify(viewTreeObserver).removeOnDrawListener(same(underTest.mOnDrawListener))
+ }
+
+ @Test
+ fun test_onInvalidSurface_multiple_times_add_onDrawListener_once() {
+ underTest.onInvalidSurface()
+ underTest.onInvalidSurface()
+ underTest.onInvalidSurface()
+
+ // We should only call addOnDrawListener 1 time
+ verify(viewTreeObserver).addOnDrawListener(same(underTest.mOnDrawListener))
+ }
+}
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
deleted file mode 100644
index 3f7c85c899..0000000000
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java
+++ /dev/null
@@ -1,265 +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.launcher3.taskbar;
-
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-
-import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.Display;
-import android.view.MotionEvent;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.apppairs.AppPairIcon;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.model.data.FolderInfo;
-import com.android.launcher3.util.ActivityContextWrapper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-/**
- * Tests for TaskbarHoverToolTipController.
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase {
-
- private TaskbarHoverToolTipController mTaskbarHoverToolTipController;
- private TestableLooper mTestableLooper;
-
- @Mock private TaskbarView mTaskbarView;
- @Mock private MotionEvent mMotionEvent;
- @Mock private BubbleTextView mHoverBubbleTextView;
- @Mock private FolderIcon mHoverFolderIcon;
- @Mock private AppPairIcon mAppPairIcon;
- @Mock private Display mDisplay;
- @Mock private TaskbarDragLayer mTaskbarDragLayer;
- private Folder mSpyFolderView;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- Context context = getApplicationContext();
-
- doAnswer((Answer<Object>) invocation -> context.getSystemService(
- (String) invocation.getArgument(0)))
- .when(taskbarActivityContext).getSystemService(anyString());
- when(taskbarActivityContext.getResources()).thenReturn(context.getResources());
- when(taskbarActivityContext.getApplicationInfo()).thenReturn(
- context.getApplicationInfo());
- when(taskbarActivityContext.getDragLayer()).thenReturn(mTaskbarDragLayer);
- when(taskbarActivityContext.getMainLooper()).thenReturn(context.getMainLooper());
- when(taskbarActivityContext.getDisplay()).thenReturn(mDisplay);
- when(taskbarActivityContext.isIconAlignedWithHotseat()).thenReturn(false);
-
- when(mTaskbarDragLayer.getChildCount()).thenReturn(1);
- mSpyFolderView = spy(new Folder(new ActivityContextWrapper(context), null));
- when(mTaskbarDragLayer.getChildAt(anyInt())).thenReturn(mSpyFolderView);
- doReturn(false).when(mSpyFolderView).isOpen();
-
- when(mHoverBubbleTextView.getText()).thenReturn("tooltip");
- doAnswer((Answer<Void>) invocation -> {
- Object[] args = invocation.getArguments();
- ((int[]) args[0])[0] = 0;
- ((int[]) args[0])[1] = 0;
- return null;
- }).when(mHoverBubbleTextView).getLocationOnScreen(any(int[].class));
- when(mHoverBubbleTextView.getWidth()).thenReturn(100);
- when(mHoverBubbleTextView.getHeight()).thenReturn(100);
-
- mHoverFolderIcon.mInfo = new FolderInfo();
- mHoverFolderIcon.mInfo.title = "tooltip";
- doAnswer((Answer<Void>) invocation -> {
- Object[] args = invocation.getArguments();
- ((int[]) args[0])[0] = 0;
- ((int[]) args[0])[1] = 0;
- return null;
- }).when(mHoverFolderIcon).getLocationOnScreen(any(int[].class));
- when(mHoverFolderIcon.getWidth()).thenReturn(100);
- when(mHoverFolderIcon.getHeight()).thenReturn(100);
-
- when(mTaskbarView.getTop()).thenReturn(200);
-
- mTaskbarHoverToolTipController = new TaskbarHoverToolTipController(
- taskbarActivityContext, mTaskbarView, mHoverBubbleTextView);
- mTestableLooper = TestableLooper.get(this);
- }
-
- @Test
- public void onHover_hoverEnterIcon_revealToolTip() {
- when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
- when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
-
- boolean hoverConsumed =
- mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent);
- waitForIdleSync();
-
- assertThat(hoverConsumed).isFalse();
- verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
- true);
- }
-
- @Test
- public void onHover_hoverExitIcon_closeToolTip() {
- when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
- when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
-
- boolean hoverConsumed =
- mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent);
- waitForIdleSync();
-
- assertThat(hoverConsumed).isFalse();
- verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
- false);
- }
-
- @Test
- public void onHover_hoverEnterFolderIcon_revealToolTip() {
- when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
- when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
-
- boolean hoverConsumed =
- mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
- waitForIdleSync();
-
- assertThat(hoverConsumed).isFalse();
- verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
- true);
- }
-
- @Test
- public void onHover_hoverExitFolderIcon_closeToolTip() {
- when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
- when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
-
- boolean hoverConsumed =
- mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
- waitForIdleSync();
-
- assertThat(hoverConsumed).isFalse();
- verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
- false);
- }
-
- @Test
- public void onHover_hoverExitFolderOpen_closeToolTip() {
- when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
- when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
- doReturn(true).when(mSpyFolderView).isOpen();
-
- boolean hoverConsumed =
- mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
- waitForIdleSync();
-
- assertThat(hoverConsumed).isFalse();
- verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
- false);
- }
-
- @Test
- public void onHover_hoverEnterFolderOpen_noToolTip() {
- when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
- when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
- doReturn(true).when(mSpyFolderView).isOpen();
-
- boolean hoverConsumed =
- mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
-
- assertThat(hoverConsumed).isFalse();
- }
-
- @Test
- public void onHover_hoverMove_noUpdate() {
- when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_MOVE);
- when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_MOVE);
-
- boolean hoverConsumed =
- mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent);
-
- assertThat(hoverConsumed).isFalse();
- }
-
- @Test
- public void onHover_hoverEnterAppPair_revealToolTip() {
- when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
- when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
-
- boolean hoverConsumed =
- mTaskbarHoverToolTipController.onHover(mAppPairIcon, mMotionEvent);
- waitForIdleSync();
-
- assertThat(hoverConsumed).isFalse();
- verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
- true);
- }
-
- @Test
- public void onHover_hoverExitAppPair_closeToolTip() {
- when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
- when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_EXIT);
-
- boolean hoverConsumed =
- mTaskbarHoverToolTipController.onHover(mAppPairIcon, mMotionEvent);
- waitForIdleSync();
-
- assertThat(hoverConsumed).isFalse();
- verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
- false);
- }
-
- @Test
- public void onHover_hoverEnterIconAlignedWithHotseat_noReveal() {
- when(mMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
- when(mMotionEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
- when(taskbarActivityContext.isIconAlignedWithHotseat()).thenReturn(true);
-
- boolean hoverConsumed =
- mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent);
- waitForIdleSync();
-
- assertThat(hoverConsumed).isFalse();
- verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS,
- true);
- }
-
- private void waitForIdleSync() {
- mTestableLooper.processAllMessages();
- }
-}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index 61a697503e..8f26795045 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -316,43 +316,6 @@ class BubbleBarViewAnimatorTest {
}
@Test
- fun animateBubbleInForStashed_showAnimationCanceled() {
- setUpBubbleBar()
-
- val handle = View(context)
- val handleAnimator = PhysicsAnimator.getInstance(handle)
- bubbleStashController.handleAnimator = handleAnimator
-
- val animator =
- BubbleBarViewAnimator(
- bubbleBarView,
- bubbleStashController,
- flyoutController,
- bubbleBarParentViewController,
- onExpanded = emptyRunnable,
- onBubbleBarVisible = emptyRunnable,
- animatorScheduler,
- )
-
- InstrumentationRegistry.getInstrumentation().runOnMainSync {
- animator.animateBubbleInForStashed(bubble, isExpanding = false)
- }
-
- // wait for the animation to start
- InstrumentationRegistry.getInstrumentation().runOnMainSync {}
- PhysicsAnimatorTestUtils.blockUntilFirstAnimationFrameWhereTrue(handleAnimator) { true }
-
- handleAnimator.assertIsRunning()
- assertThat(animator.isAnimating).isTrue()
- assertThat(animatorScheduler.delayedBlock).isNotNull()
-
- handleAnimator.cancel()
- handleAnimator.assertIsNotRunning()
- assertThat(animator.isAnimating).isFalse()
- assertThat(animatorScheduler.delayedBlock).isNull()
- }
-
- @Test
fun animateBubbleInForStashed_autoExpanding() {
setUpBubbleBar()
diff --git a/quickstep/tests/src/com/android/quickstep/AspectRatioSystemShortcutTests.kt b/quickstep/tests/src/com/android/quickstep/AspectRatioSystemShortcutTests.kt
new file mode 100644
index 0000000000..10e85e6a1b
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/AspectRatioSystemShortcutTests.kt
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.provider.Settings
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.LayoutInflater
+import android.view.Surface
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.AbstractFloatingViewHelper
+import com.android.launcher3.Flags.enableRefactorTaskThumbnail
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.logging.StatsLogManager
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent
+import com.android.launcher3.logging.StatsLogManager.StatsLogger
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.model.data.TaskViewItemInfo
+import com.android.launcher3.util.RunnableList
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.TransformingTouchDelegate
+import com.android.launcher3.util.WindowBounds
+import com.android.quickstep.orientation.LandscapePagedViewHandler
+import com.android.quickstep.recents.data.RecentsDeviceProfileRepository
+import com.android.quickstep.recents.data.RecentsRotationStateRepository
+import com.android.quickstep.recents.di.RecentsDependencies
+import com.android.quickstep.task.thumbnail.TaskThumbnailView
+import com.android.quickstep.util.RecentsOrientedState
+import com.android.quickstep.views.LauncherRecentsView
+import com.android.quickstep.views.RecentsViewContainer
+import com.android.quickstep.views.TaskContainer
+import com.android.quickstep.views.TaskThumbnailViewDeprecated
+import com.android.quickstep.views.TaskView
+import com.android.quickstep.views.TaskViewIcon
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.Task.TaskKey
+import com.android.window.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mockito
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.isNull
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/** Test for [AspectRatioSystemShortcut] */
+class AspectRatioSystemShortcutTests {
+
+ @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
+ /** Spy on a concrete Context so we can reference real View, Layout, and Display properties. */
+ private val context: Context = spy(InstrumentationRegistry.getInstrumentation().targetContext)
+
+ /**
+ * RecentsViewContainer and its super-interface ActivityContext contain methods to convert
+ * themselves to a Context at runtime, and static methods to convert a Context back to
+ * themselves by traversing ContextWrapper layers.
+ *
+ * Thus there is an undocumented assumption that a RecentsViewContainer always extends Context.
+ * We need to mock all of the RecentsViewContainer methods but leave the Context-under-test
+ * intact.
+ *
+ * The simplest way is to extend ContextWrapper and delegate the RecentsViewContainer interface
+ * to a mock.
+ */
+ class RecentsViewContainerContextWrapper(base: Context) :
+ ContextWrapper(base), RecentsViewContainer by mock() {
+
+ private val statsLogManager: StatsLogManager = mock()
+
+ override fun getStatsLogManager(): StatsLogManager = statsLogManager
+
+ override fun startActivitySafely(v: View, intent: Intent, item: ItemInfo?): RunnableList? =
+ null
+ }
+
+ /**
+ * This <RecentsViewContainer & Context> is implicitly required in many parts of Launcher that
+ * require a Context. See RecentsViewContainerContextWrapper.
+ */
+ private val launcher: RecentsViewContainerContextWrapper =
+ spy(RecentsViewContainerContextWrapper(context))
+
+ private val recentsView: LauncherRecentsView = mock()
+ private val abstractFloatingViewHelper: AbstractFloatingViewHelper = mock()
+ private val taskOverlayFactory: TaskOverlayFactory =
+ mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS)
+ private val factory: TaskShortcutFactory =
+ AspectRatioSystemShortcut.createFactory(abstractFloatingViewHelper)
+ private val statsLogger = mock<StatsLogger>()
+ private val orientedState: RecentsOrientedState =
+ mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS)
+ private val taskView: TaskView =
+ LayoutInflater.from(context).cloneInContext(launcher).inflate(R.layout.task, null) as
+ TaskView
+
+ @Before
+ fun setUp() {
+ whenever(launcher.getOverviewPanel<LauncherRecentsView>()).thenReturn(recentsView)
+
+ val statsLogManager = launcher.getStatsLogManager()
+ whenever(statsLogManager.logger()).thenReturn(statsLogger)
+ whenever(statsLogger.withItemInfo(any())).thenReturn(statsLogger)
+
+ whenever(orientedState.orientationHandler).thenReturn(LandscapePagedViewHandler())
+ taskView.setLayoutParams(ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT))
+
+ if (enableRefactorTaskThumbnail()) {
+ val recentsDependencies = RecentsDependencies.maybeInitialize(launcher)
+ val scopeId = recentsDependencies.createRecentsViewScope(launcher)
+ recentsDependencies.provide(
+ RecentsRotationStateRepository::class.java,
+ scopeId,
+ { mock<RecentsRotationStateRepository>() }
+ )
+ recentsDependencies.provide(
+ RecentsDeviceProfileRepository::class.java,
+ scopeId,
+ { mock<RecentsDeviceProfileRepository>() }
+ )
+ }
+ }
+
+ @After
+ fun tearDown() {
+ if (enableRefactorTaskThumbnail()) {
+ RecentsDependencies.destroy(launcher)
+ }
+ }
+
+ /**
+ * When the corresponding feature flag is off, there will not be an option to open aspect ratio
+ * settings.
+ */
+ @DisableFlags(com.android.window.flags.Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT)
+ @Test
+ fun createShortcut_flaggedOff_notCreated() {
+ val task = createTask()
+ val taskContainer = createTaskContainer(task)
+
+ setScreenSizeDp(widthDp = 1200, heightDp = 800)
+ taskView.bind(task, orientedState, taskOverlayFactory)
+
+ assertThat(factory.getShortcuts(launcher, taskContainer)).isNull()
+ }
+
+ /**
+ * When the screen doesn't meet or exceed sw600dp (eg. phone, watch), there will not be an
+ * option to open aspect ratio settings.
+ */
+ @EnableFlags(com.android.window.flags.Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT)
+ @Test
+ fun createShortcut_sw599dp_notCreated() {
+ val task = createTask()
+ val taskContainer = createTaskContainer(task)
+
+ setScreenSizeDp(widthDp = 599, heightDp = 599)
+ taskView.bind(task, orientedState, taskOverlayFactory)
+
+ assertThat(factory.getShortcuts(launcher, taskContainer)).isNull()
+ }
+
+ /**
+ * When the screen does meet or exceed sw600dp (eg. tablet, inner foldable screen, home cinema)
+ * there will be an option to open aspect ratio settings.
+ */
+ @EnableFlags(com.android.window.flags.Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT)
+ @Test
+ fun createShortcut_sw800dp_created_andOpensSettings() {
+ val task = createTask()
+ val taskContainer = spy(createTaskContainer(task))
+ val taskViewItemInfo = mock<TaskViewItemInfo>()
+ doReturn(taskViewItemInfo).whenever(taskContainer).itemInfo
+
+ setScreenSizeDp(widthDp = 1200, heightDp = 800)
+ taskView.bind(task, orientedState, taskOverlayFactory)
+
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).hasSize(1)
+
+ // On clicking the shortcut:
+ val shortcut = shortcuts!!.first() as AspectRatioSystemShortcut
+ shortcut.onClick(taskView)
+
+ // 1) Panel should be closed
+ val allTypesExceptRebindSafe =
+ AbstractFloatingView.TYPE_ALL and AbstractFloatingView.TYPE_REBIND_SAFE.inv()
+ verify(abstractFloatingViewHelper).closeOpenViews(launcher, true, allTypesExceptRebindSafe)
+
+ // 2) Compat mode settings activity should be launched
+ val intentCaptor = argumentCaptor<Intent>()
+ verify(launcher)
+ .startActivitySafely(any<View>(), intentCaptor.capture(), eq(taskViewItemInfo))
+ val intent = intentCaptor.firstValue!!
+ assertThat(intent.action).isEqualTo(Settings.ACTION_MANAGE_USER_ASPECT_RATIO_SETTINGS)
+
+ // 3) Shortcut tap event should be reported
+ verify(statsLogger).withItemInfo(taskViewItemInfo)
+ verify(statsLogger).log(LauncherEvent.LAUNCHER_ASPECT_RATIO_SETTINGS_SYSTEM_SHORTCUT_TAP)
+ }
+
+ /**
+ * Overrides the screen size reported in the DeviceProfile, keeping the same pixel density as
+ * the underlying device and adjusting the pixel width/height to match what is required.
+ */
+ private fun setScreenSizeDp(widthDp: Int, heightDp: Int) {
+ val density = context.resources.configuration.densityDpi
+ val widthPx = widthDp * density / 160
+ val heightPx = heightDp * density / 160
+
+ val screenBounds = WindowBounds(widthPx, heightPx, widthPx, heightPx, Surface.ROTATION_0)
+ val deviceProfile =
+ InvariantDeviceProfile.INSTANCE[context].getDeviceProfile(context)
+ .toBuilder(context)
+ .setWindowBounds(screenBounds)
+ .build()
+ whenever(launcher.getDeviceProfile()).thenReturn(deviceProfile)
+ }
+
+ /** Create a (very) fake task for testing. */
+ private fun createTask() =
+ Task(
+ TaskKey(
+ /* id */ 1,
+ /* windowingMode */ 0,
+ Intent(),
+ ComponentName("", ""),
+ /* userId */ 0,
+ /* lastActiveTime */ 2000,
+ DEFAULT_DISPLAY,
+ ComponentName("", ""),
+ /* numActivities */ 1,
+ /* isTopActivityNoDisplay */ false,
+ /* isActivityStackTransparent */ false,
+ )
+ )
+
+ /** Create TaskContainer out of a given Task and fill in the rest with mocks. */
+ private fun createTaskContainer(task: Task) =
+ TaskContainer(
+ taskView,
+ task,
+ if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
+ else mock<TaskThumbnailViewDeprecated>(),
+ mock<TaskViewIcon>(),
+ mock<TransformingTouchDelegate>(),
+ SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
+ digitalWellBeingToast = null,
+ showWindowsView = null,
+ taskOverlayFactory,
+ )
+}
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index 7b73be7ac4..5f61ba2e07 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -22,6 +22,7 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.view.Display.DEFAULT_DISPLAY
import androidx.test.platform.app.InstrumentationRegistry
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
@@ -36,7 +37,6 @@ import com.android.launcher3.model.data.TaskViewItemInfo
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
-import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.LauncherRecentsView
import com.android.quickstep.views.RecentsViewContainer
@@ -52,6 +52,7 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito.`when`
import org.mockito.kotlin.any
@@ -64,8 +65,11 @@ import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
/** Test for [DesktopSystemShortcut] */
+// TODO(b/403558856): Improve test coverage for DesktopModeCompatPolicy integration.
class DesktopSystemShortcutTest {
+ @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
private val launcher: RecentsViewContainer = mock()
private val statsLogManager: StatsLogManager = mock()
private val statsLogger: StatsLogManager.StatsLogger = mock()
@@ -249,7 +253,6 @@ class DesktopSystemShortcutTest {
TaskContainer(
taskView,
task,
- mock<TaskContentView>(),
if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
else mock<TaskThumbnailViewDeprecated>(),
mock<TaskViewIcon>(),
diff --git a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
index 5aaed7dd25..2db94f6f1a 100644
--- a/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/ExternalDisplaySystemShortcutTest.kt
@@ -37,7 +37,6 @@ import com.android.launcher3.model.data.TaskViewItemInfo
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.quickstep.TaskOverlayFactory.TaskOverlay
-import com.android.quickstep.task.thumbnail.TaskContentView
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.views.LauncherRecentsView
import com.android.quickstep.views.RecentsViewContainer
@@ -247,7 +246,6 @@ class ExternalDisplaySystemShortcutTest {
TaskContainer(
taskView,
task,
- mock<TaskContentView>(),
if (enableRefactorTaskThumbnail()) mock<TaskThumbnailView>()
else mock<TaskThumbnailViewDeprecated>(),
mock<TaskViewIcon>(),
diff --git a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
index ef6f55e24f..1464ca8432 100644
--- a/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/InputConsumerUtilsTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Looper;
import android.view.Choreographer;
import android.view.Display;
@@ -100,12 +99,13 @@ public class InputConsumerUtilsTest {
@Rule public final SandboxApplication mContext = new SandboxApplication();
+ private final int mDisplayId = Display.DEFAULT_DISPLAY;
@NonNull private final InputMonitorCompat mInputMonitorCompat =
- new InputMonitorCompat("", Display.DEFAULT_DISPLAY);
+ new InputMonitorCompat("", mDisplayId);
private TaskAnimationManager mTaskAnimationManager;
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
- @Nullable private ResetGestureInputConsumer mResetGestureInputConsumer;
+ private boolean mUserUnlocked = true;
@NonNull private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = (state) -> null;
@NonNull @Mock private TaskbarActivityContext mTaskbarActivityContext;
@@ -126,8 +126,7 @@ public class InputConsumerUtilsTest {
@Before
public void setupTaskAnimationManager() {
- mTaskAnimationManager = new TaskAnimationManager(
- mContext, mDeviceState);
+ mTaskAnimationManager = new TaskAnimationManager(mContext, mDeviceState, mDisplayId);
}
@Before
@@ -164,12 +163,6 @@ public class InputConsumerUtilsTest {
}
@Before
- public void setUpResetGestureInputConsumer() {
- mResetGestureInputConsumer = new ResetGestureInputConsumer(
- mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
- }
-
- @Before
public void setupLockedUserState() {
when(mLockedUserState.isUserUnlocked()).thenReturn(true);
}
@@ -300,7 +293,7 @@ public class InputConsumerUtilsTest {
@Test
public void testNewBaseConsumer_launcherChildActivityResumed_returnsDefaultInputConsumer() {
when(mRunningTask.isHomeTask()).thenReturn(true);
- when(mOverviewComponentObserver.isHomeAndOverviewSame()).thenReturn(true);
+ when(mOverviewComponentObserver.isHomeAndOverviewSameActivity()).thenReturn(true);
assertEqualsDefaultInputConsumer(this::createBaseInputConsumer);
}
@@ -314,8 +307,6 @@ public class InputConsumerUtilsTest {
@Test
public void testNewBaseConsumer_containsOtherActivityInputConsumer() {
- // OtherActivityInputConsumer needs to be initialized on the main thread because of
- // MotionPauseDetector.mForcePauseTimeout
assertCorrectInputConsumer(
this::createBaseInputConsumer,
OtherActivityInputConsumer.class,
@@ -486,7 +477,7 @@ public class InputConsumerUtilsTest {
MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
InputConsumer inputConsumer = newConsumer(
mContext,
- mResetGestureInputConsumer,
+ mUserUnlocked,
mOverviewComponentObserver,
mDeviceState,
mPreviousGestureState,
@@ -510,7 +501,8 @@ public class InputConsumerUtilsTest {
MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
InputConsumer inputConsumer = newBaseConsumer(
mContext,
- mResetGestureInputConsumer,
+ mUserUnlocked,
+ mTaskbarManager,
mOverviewComponentObserver,
mDeviceState,
mPreviousGestureState,
@@ -535,12 +527,15 @@ public class InputConsumerUtilsTest {
ResetGestureInputConsumer.class,
InputConsumer.TYPE_RESET_GESTURE);
- mResetGestureInputConsumer = null;
+ mUserUnlocked = false;
- runOnMainSync(() -> assertThat(inputConsumerProvider.get()).isEqualTo(InputConsumer.NO_OP));
+ assertCorrectInputConsumer(
+ inputConsumerProvider,
+ InputConsumer.class,
+ InputConsumer.TYPE_NO_OP);
}
- private static void assertCorrectInputConsumer(
+ private void assertCorrectInputConsumer(
@NonNull Provider<InputConsumer> inputConsumerProvider,
@NonNull Class<? extends InputConsumer> expectedOutputConsumer,
int expectedType) {
@@ -551,11 +546,13 @@ public class InputConsumerUtilsTest {
expectedType);
}
- private static void assertCorrectInputConsumer(
+ private void assertCorrectInputConsumer(
@NonNull Provider<InputConsumer> inputConsumerProvider,
@NonNull Class<? extends InputConsumer> expectedOutputConsumer,
@NonNull Class<? extends InputConsumer> expectedActiveConsumer,
int expectedType) {
+ when(mCurrentGestureState.getDisplayId()).thenReturn(mDisplayId);
+
runOnMainSync(() -> {
InputConsumer inputConsumer = inputConsumerProvider.get();
@@ -563,7 +560,14 @@ public class InputConsumerUtilsTest {
assertThat(inputConsumer.getActiveConsumerInHierarchy())
.isInstanceOf(expectedActiveConsumer);
assertThat(inputConsumer.getType()).isEqualTo(expectedType);
+ assertThat(inputConsumer.getDisplayId()).isEqualTo(mDisplayId);
});
+ int expectedDisplayId = mDisplayId + 1;
+
+ when(mCurrentGestureState.getDisplayId()).thenReturn(expectedDisplayId);
+
+ runOnMainSync(() -> assertThat(inputConsumerProvider.get().getDisplayId())
+ .isEqualTo(expectedDisplayId));
}
private static void runOnMainSync(@NonNull Runnable runnable) {
diff --git a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
index 47108e0572..daa77d2a31 100644
--- a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
@@ -32,7 +32,9 @@ import android.util.DisplayMetrics
import android.view.SurfaceControl
import android.view.WindowManager
import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
import androidx.core.util.Supplier
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
import com.android.app.animation.Interpolators
import com.android.internal.jank.Cuj
import com.android.launcher3.desktop.DesktopAppLaunchAnimatorHelper
@@ -45,6 +47,7 @@ import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
class DesktopAppLaunchAnimatorHelperTest {
@@ -70,6 +73,10 @@ class DesktopAppLaunchAnimatorHelperTest {
whenever(transactionSupplier.get()).thenReturn(transaction)
whenever(transaction.setCrop(any(), any())).thenReturn(transaction)
whenever(transaction.setCornerRadius(any(), any())).thenReturn(transaction)
+ whenever(transaction.setScale(any(), any(), any())).thenReturn(transaction)
+ whenever(transaction.setPosition(any(), any(), any())).thenReturn(transaction)
+ whenever(transaction.setAlpha(any(), any())).thenReturn(transaction)
+ whenever(transaction.setFrameTimeline(any())).thenReturn(transaction)
whenever(context.resources).thenReturn(resources)
whenever(resources.displayMetrics).thenReturn(DisplayMetrics())
@@ -77,14 +84,8 @@ class DesktopAppLaunchAnimatorHelperTest {
}
@Test
- fun launchTransition_returnsLaunchAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
+ fun launchTransition_returnsLaunchAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -93,14 +94,27 @@ class DesktopAppLaunchAnimatorHelperTest {
}
@Test
- fun noLaunchTransition_returnsEmptyAnimatorsList() {
+ fun launchTransition_callsAnimationEndListener() = runOnUiThread {
+ val finishCallback = mock<Function1<Animator, Unit>>()
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE))
+
+ val animators = helper.createAnimators(transitionInfo, finishCallback = finishCallback)
+
+ animators.forEach { animator ->
+ animator.start()
+ animator.end()
+ verify(finishCallback).invoke(animator)
+ }
+ }
+
+ @Test
+ fun noLaunchTransition_returnsEmptyAnimatorsList() = runOnUiThread {
val pipChange =
TransitionInfo.Change(mock(), mock()).apply {
mode = WindowManager.TRANSIT_PIP
taskInfo = TASK_INFO_FREEFORM
}
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(pipChange)
+ val transitionInfo = createTransitionInfo(listOf(pipChange))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -108,20 +122,8 @@ class DesktopAppLaunchAnimatorHelperTest {
}
@Test
- fun minimizeTransition_returnsLaunchAndMinimizeAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val minimizeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_TO_BACK
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(minimizeChange)
+ fun minimizeTransition_returnsLaunchAndMinimizeAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, MINIMIZE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -131,21 +133,23 @@ class DesktopAppLaunchAnimatorHelperTest {
}
@Test
+ fun minimizeTransition_callsAnimationEndListener() = runOnUiThread {
+ val finishCallback = mock<Function1<Animator, Unit>>()
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, MINIMIZE_CHANGE))
+
+ val animators = helper.createAnimators(transitionInfo, finishCallback = finishCallback)
+
+ animators.forEach { animator ->
+ animator.start()
+ animator.end()
+ verify(finishCallback).invoke(animator)
+ }
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagEnabled_returnsLaunchAndCloseAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagEnabled_returnsLaunchAndCloseAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -155,21 +159,24 @@ class DesktopAppLaunchAnimatorHelperTest {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
+ fun trampolineTransition_flagEnabled_callsAnimationEndListener() = runOnUiThread {
+ val finishCallback = mock<Function1<Animator, Unit>>()
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, CLOSE_CHANGE))
+
+ val animators = helper.createAnimators(transitionInfo, finishCallback = finishCallback)
+
+ animators.forEach { animator ->
+ animator.start()
+ animator.end()
+ verify(finishCallback).invoke(animator)
+ }
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagDisabled_returnsLaunchAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagDisabled_returnsLaunchAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -179,26 +186,9 @@ class DesktopAppLaunchAnimatorHelperTest {
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagEnabled_hitDesktopWindowLimit_returnsLaunchMinimizeCloseAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val minimizeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_TO_BACK
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(minimizeChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagEnabled_hitDesktopWindowLimit_returnsLaunchMinimizeCloseAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(
+ listOf(OPEN_CHANGE, MINIMIZE_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -210,26 +200,9 @@ class DesktopAppLaunchAnimatorHelperTest {
@Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagDisabled_hitDesktopWindowLimit_returnsLaunchMinimizeAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val minimizeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_TO_BACK
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(minimizeChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagDisabled_hitDesktopWindowLimit_returnsLaunchMinimizeAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(
+ listOf(OPEN_CHANGE, MINIMIZE_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -280,6 +253,12 @@ class DesktopAppLaunchAnimatorHelperTest {
assertThat(animator.duration).isEqualTo(100)
}
+ private fun createTransitionInfo(changes: List<Change>): TransitionInfo {
+ val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
+ changes.forEach { transitionInfo.addChange(it) }
+ return transitionInfo
+ }
+
private companion object {
val TASK_INFO_FREEFORM =
ActivityManager.RunningTaskInfo().apply {
@@ -290,5 +269,23 @@ class DesktopAppLaunchAnimatorHelperTest {
configuration.windowConfiguration.windowingMode =
WindowConfiguration.WINDOWING_MODE_FREEFORM
}
+
+ val OPEN_CHANGE =
+ TransitionInfo.Change(mock(), mock()).apply {
+ mode = WindowManager.TRANSIT_OPEN
+ taskInfo = TASK_INFO_FREEFORM
+ }
+
+ val CLOSE_CHANGE =
+ TransitionInfo.Change(mock(), mock()).apply {
+ mode = WindowManager.TRANSIT_CLOSE
+ taskInfo = TASK_INFO_FREEFORM
+ }
+
+ val MINIMIZE_CHANGE =
+ TransitionInfo.Change(mock(), mock()).apply {
+ mode = WindowManager.TRANSIT_TO_BACK
+ taskInfo = TASK_INFO_FREEFORM
+ }
}
}