From 4078347cbfb0ae7e67d71a9c1b2e3f9d6e33e85a Mon Sep 17 00:00:00 2001 From: Ajinkya Chalke Date: Tue, 4 Feb 2025 03:38:23 -0800 Subject: [PATCH 1/2] [CD][Alt+Tab] Implement skeleton code for KQS on CD - This change will not render KQS on connected display (CD) even with the flag ON as taskbars are not yet created for connected displays. Bug: 382762871 Change-Id: Id59fb23630aaf0e74c35818f2a4ca56e5ef2e7bb Flag: com.android.launcher3.enable_alt_tab_kqs_on_connected_displays Test: manually built and run the CUJ --- .../taskbar/TaskbarActivityContext.java | 5 +- .../launcher3/taskbar/TaskbarManager.java | 42 ++++++++++++++++- .../src/com/android/quickstep/FocusState.kt | 9 ++-- .../quickstep/OverviewCommandHelper.kt | 47 ++++++++++++++++++- .../com/android/quickstep/SystemUiProxy.kt | 2 +- .../quickstep/TouchInteractionService.java | 8 +++- .../fallback/window/RecentsDisplayModel.kt | 6 ++- .../taskbar/rules/TaskbarUnitTestRule.kt | 2 + .../quickstep/OverviewCommandHelperTest.kt | 5 +- 9 files changed, 112 insertions(+), 14 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 9d1fc15693..a6d3cde387 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -24,6 +24,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED; +import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE; + import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_ON_BOARD_POPUP; import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; @@ -2008,7 +2010,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return mControllers.uiController.isIconAlignedWithHotseat(); } - @VisibleForTesting + // TODO(b/395061396): Remove `otherwise` when overview in widow is enabled. + @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public TaskbarControllers getControllers() { return mControllers; } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index ea2dec1f17..028e9e7f61 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -32,6 +32,7 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange; import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR; import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR; +import static com.android.window.flags.Flags.enableTaskbarConnectedDisplays; import android.annotation.SuppressLint; import android.app.PendingIntent; @@ -71,6 +72,7 @@ import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.quickstep.AllAppsActionManager; import com.android.quickstep.RecentsActivity; import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.fallback.window.RecentsDisplayModel; import com.android.quickstep.fallback.window.RecentsWindowManager; import com.android.quickstep.util.ContextualSearchInvoker; import com.android.quickstep.views.RecentsViewContainer; @@ -182,6 +184,7 @@ public class TaskbarManager { new SimpleBroadcastReceiver(UI_HELPER_EXECUTOR, this::showTaskbarFromBroadcast); private final AllAppsActionManager mAllAppsActionManager; + private final RecentsDisplayModel mRecentsDisplayModel; private final Runnable mActivityOnDestroyCallback = new Runnable() { @Override @@ -242,10 +245,12 @@ public class TaskbarManager { public TaskbarManager( Context context, AllAppsActionManager allAppsActionManager, - TaskbarNavButtonCallbacks navCallbacks) { + TaskbarNavButtonCallbacks navCallbacks, + RecentsDisplayModel recentsDisplayModel) { mParentContext = context; createWindowContext(context.getDisplayId()); mAllAppsActionManager = allAppsActionManager; + mRecentsDisplayModel = recentsDisplayModel; if (enableTaskbarNoRecreate()) { createTaskbarRootLayout(getDefaultDisplayId()); } @@ -487,6 +492,18 @@ public class TaskbarManager { return null; } + /** Creates a {@link TaskbarUIController} to use with non default displays. */ + private TaskbarUIController createTaskbarUIControllerForNonDefaultDisplay(int displayId) { + if (RecentsDisplayModel.enableOverviewInWindow()) { + RecentsViewContainer rvc = mRecentsDisplayModel.getRecentsWindowManager(displayId); + if (rvc != null) { + return createTaskbarUIControllerForRecentsViewContainer(rvc); + } + } + + return new TaskbarUIController(); + } + /** * Creates a {@link TaskbarUIController} to use while the given StatefulActivity is active. */ @@ -561,7 +578,11 @@ public class TaskbarManager { mSharedState.allAppsVisible = mSharedState.allAppsVisible && isLargeScreenTaskbar; taskbar.init(mSharedState); - if (mRecentsViewContainer != null) { + // Non default displays should not use LauncherTaskbarUIController as they shouldn't + // have access to the Launcher activity. + if (enableTaskbarConnectedDisplays() && !isDefaultDisplay(displayId)) { + taskbar.setUIController(createTaskbarUIControllerForNonDefaultDisplay(displayId)); + } else if (mRecentsViewContainer != null) { taskbar.setUIController( createTaskbarUIControllerForRecentsViewContainer(mRecentsViewContainer)); } @@ -828,6 +849,23 @@ public class TaskbarManager { } } + /** + * Returns the {@link TaskbarUIController} associated with the given display ID. + * TODO(b/395061396): Remove this method when overview in widow is enabled. + * + * @param displayId The ID of the display to retrieve the taskbar for. + * @return The {@link TaskbarUIController} for the specified display, or + * {@code null} if no taskbar is associated with that display. + */ + @Nullable + public TaskbarUIController getUIControllerForDisplay(int displayId) { + if (!mTaskbars.contains(displayId)) { + return null; + } + + return getTaskbarForDisplay(displayId).getControllers().uiController; + } + /** * Retrieves whether RootLayout was added to window for specific display, or false if no * such mapping has been made. diff --git a/quickstep/src/com/android/quickstep/FocusState.kt b/quickstep/src/com/android/quickstep/FocusState.kt index ba3991fdff..7c6aa5b605 100644 --- a/quickstep/src/com/android/quickstep/FocusState.kt +++ b/quickstep/src/com/android/quickstep/FocusState.kt @@ -27,7 +27,10 @@ import com.android.wm.shell.shared.IShellTransitions class FocusState { var focusedDisplayId = DEFAULT_DISPLAY - private set + private set(value) { + field = value + listeners.forEach { it.onFocusedDisplayChanged(value) } + } private var listeners = mutableSetOf() @@ -40,9 +43,7 @@ class FocusState { transitions?.setFocusTransitionListener( object : Stub() { override fun onFocusedDisplayChanged(displayId: Int) { - Executors.MAIN_EXECUTOR.execute { - listeners.forEach { it.onFocusedDisplayChanged(displayId) } - } + Executors.MAIN_EXECUTOR.execute { focusedDisplayId = displayId } } } ) diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt index 94d115bf54..42aa86e9e0 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt @@ -28,6 +28,7 @@ import androidx.annotation.BinderThread import androidx.annotation.UiThread import androidx.annotation.VisibleForTesting import com.android.internal.jank.Cuj +import com.android.launcher3.Flags.enableAltTabKqsOnConnectedDisplays import com.android.launcher3.Flags.enableFallbackOverviewInWindow import com.android.launcher3.Flags.enableLargeDesktopWindowingTile import com.android.launcher3.Flags.enableLauncherOverviewInWindow @@ -38,6 +39,8 @@ import com.android.launcher3.logging.StatsLogManager import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_3_BUTTON import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_QUICK_SWITCH import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_SHOW_OVERVIEW_FROM_KEYBOARD_SHORTCUT +import com.android.launcher3.taskbar.TaskbarManager +import com.android.launcher3.taskbar.TaskbarUIController import com.android.launcher3.util.Executors import com.android.launcher3.util.RunnableList import com.android.launcher3.util.coroutines.DispatcherProvider @@ -48,6 +51,7 @@ import com.android.quickstep.OverviewCommandHelper.CommandType.HOME import com.android.quickstep.OverviewCommandHelper.CommandType.KEYBOARD_INPUT import com.android.quickstep.OverviewCommandHelper.CommandType.SHOW import com.android.quickstep.OverviewCommandHelper.CommandType.TOGGLE +import com.android.quickstep.fallback.window.RecentsDisplayModel import com.android.quickstep.util.ActiveGestureLog import com.android.quickstep.util.ActiveGestureProtoLogProxy import com.android.quickstep.views.RecentsView @@ -72,6 +76,9 @@ constructor( private val overviewComponentObserver: OverviewComponentObserver, private val taskAnimationManager: TaskAnimationManager, private val dispatcherProvider: DispatcherProvider = ProductionDispatchers, + private val recentsDisplayModel: RecentsDisplayModel, + private val focusState: FocusState, + private val taskbarManager: TaskbarManager, ) { private val coroutineScope = CoroutineScope(SupervisorJob() + dispatcherProvider.background) @@ -291,15 +298,51 @@ constructor( deviceProfile != null && (deviceProfile.isTablet || deviceProfile.isTwoPanels) + val focusedDisplayId = focusState.focusedDisplayId + val focusedDisplayUIController: TaskbarUIController? = + if (RecentsDisplayModel.enableOverviewInWindow()) { + Log.d( + TAG, + "Querying RecentsDisplayModel for TaskbarUIController for display: $focusedDisplayId", + ) + recentsDisplayModel.getRecentsWindowManager(focusedDisplayId)?.taskbarUIController + } else { + Log.d( + TAG, + "Querying TaskbarManager for TaskbarUIController for display: $focusedDisplayId", + ) + // TODO(b/395061396): Remove this path when overview in widow is enabled. + taskbarManager.getUIControllerForDisplay(focusedDisplayId) + } + Log.d( + TAG, + "TaskbarUIController for display $focusedDisplayId was" + + "${if (focusedDisplayUIController == null) " not" else ""} found", + ) + when (command.type) { HIDE -> { if (!allowQuickSwitch) return true - keyboardTaskFocusIndex = uiController!!.launchFocusedTask() + keyboardTaskFocusIndex = + if ( + enableAltTabKqsOnConnectedDisplays() && focusedDisplayUIController != null + ) { + focusedDisplayUIController.launchFocusedTask() + } else { + uiController!!.launchFocusedTask() + } + if (keyboardTaskFocusIndex == -1) return true } KEYBOARD_INPUT -> if (allowQuickSwitch) { - uiController!!.openQuickSwitchView() + if ( + enableAltTabKqsOnConnectedDisplays() && focusedDisplayUIController != null + ) { + focusedDisplayUIController.openQuickSwitchView() + } else { + uiController!!.openQuickSwitchView() + } return true } else { keyboardTaskFocusIndex = 0 diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt index d5382ad21a..a1ac39e354 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.kt +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt @@ -149,7 +149,7 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context: private var backToLauncherRunner: IRemoteAnimationRunner? = null private var dragAndDrop: IDragAndDrop? = null val homeVisibilityState = HomeVisibilityState() - private val focusState = FocusState() + val focusState = FocusState() // Used to dedupe calls to SystemUI private var lastShelfHeight = 0 diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 8d0834c53c..8422d4c223 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -88,6 +88,7 @@ import com.android.launcher3.util.ScreenOnTracker; import com.android.launcher3.util.TraceHelper; 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.RecentsWindowSwipeHandler; import com.android.quickstep.inputconsumers.BubbleBarInputConsumer; import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer; @@ -582,7 +583,8 @@ public class TouchInteractionService extends Service { initInputMonitor("onTrackpadConnected()"); }); - mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks); + mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks, + RecentsDisplayModel.getINSTANCE().get(this)); mDesktopAppLaunchTransitionManager = new DesktopAppLaunchTransitionManager(this, SystemUiProxy.INSTANCE.get(this)); mDesktopAppLaunchTransitionManager.registerTransitions(); @@ -638,7 +640,9 @@ public class TouchInteractionService extends Service { mTaskAnimationManager = new TaskAnimationManager(this, mDeviceState); mOverviewComponentObserver = OverviewComponentObserver.INSTANCE.get(this); mOverviewCommandHelper = new OverviewCommandHelper(this, - mOverviewComponentObserver, mTaskAnimationManager); + mOverviewComponentObserver, mTaskAnimationManager, + RecentsDisplayModel.getINSTANCE().get(this), + SystemUiProxy.INSTANCE.get(this).getFocusState(), mTaskbarManager); mResetGestureInputConsumer = new ResetGestureInputConsumer( mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext); mInputConsumer.registerInputConsumer(); diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt index 31a1be8680..95a3ec2b86 100644 --- a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt +++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt @@ -50,10 +50,14 @@ constructor( DaggerSingletonObject( QuickstepBaseAppComponent::getRecentsDisplayModel ) + + @JvmStatic + fun enableOverviewInWindow() = + Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow() } init { - if (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()) { + if (enableOverviewInWindow()) { displayManager.registerDisplayListener(displayListener, Executors.MAIN_EXECUTOR.handler) // In the scenario where displays were added before this display listener was // registered, we should store the RecentsDisplayResources for those displays 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 90c9553aad..0204b2de21 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 @@ -33,6 +33,7 @@ import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR import com.android.launcher3.util.TestUtil import com.android.quickstep.AllAppsActionManager +import com.android.quickstep.fallback.window.RecentsDisplayModel import java.lang.reflect.Field import java.lang.reflect.ParameterizedType import java.util.Locale @@ -110,6 +111,7 @@ class TaskbarUnitTestRule( PendingIntent(IIntentSender.Default()) }, object : TaskbarNavButtonCallbacks {}, + RecentsDisplayModel.INSTANCE.get(context), ) { override fun recreateTaskbar() { super.recreateTaskbar() diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt index 0ae710f866..56c01f94a5 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/OverviewCommandHelperTest.kt @@ -68,7 +68,10 @@ class OverviewCommandHelperTest { touchInteractionService = mock(), overviewComponentObserver = mock(), taskAnimationManager = mock(), - dispatcherProvider = TestDispatcherProvider(dispatcher) + dispatcherProvider = TestDispatcherProvider(dispatcher), + recentsDisplayModel = mock(), + focusState = mock(), + taskbarManager = mock(), ) ) From 3ef91e087dc81bc95efa96e56a0c03a82ca95e61 Mon Sep 17 00:00:00 2001 From: Ajinkya Chalke Date: Wed, 5 Feb 2025 17:16:46 +0000 Subject: [PATCH 2/2] [CD][Alt+Tab] Show apps from all desktops for KQS on CD - Even without multiple desktops, apps that are moved to connected display show up in separate desktop task. - Further changes may be required to launch a desktop after multiple desktops is implemented. Bug: 382762871 Flag: com.android.launcher3.enable_alt_tab_kqs_on_connected_displays Test: manually built and run the CUJ Change-Id: If104c67d95a1ec3de404998b06352edfa02c0f09 --- .../KeyboardQuickSwitchController.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java index 23065b5f60..5afc5eda6d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.taskbar; +import static com.android.launcher3.Flags.enableAltTabKqsOnConnectedDisplays; + import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.view.MotionEvent; @@ -272,11 +274,26 @@ public final class KeyboardQuickSwitchController implements } private void processLoadedTasksOnDesktop(List tasks, Set taskIdsToExclude) { - // Find the single desktop task that contains a grouping of desktop tasks - DesktopTask desktopTask = findDesktopTask(tasks); + // Find all desktop tasks. + List desktopTasks = tasks.stream() + .filter(t -> t instanceof DesktopTask) + .map(t -> (DesktopTask) t) + .toList(); - if (desktopTask != null) { - mTasks = desktopTask.getTasks().stream() + // Apps on the connected displays seem to be in different Desktop tasks even with the + // multiple desktops flag disabled. So, until multiple desktops is implemented the following + // should help with team-fooding Alt+tab on connected displays. Post multiple desktop, + // further changes maybe required to support launching selected desktops. + if (enableAltTabKqsOnConnectedDisplays()) { + mTasks = desktopTasks.stream() + .flatMap(t -> t.getTasks().stream()) + .map(SingleTask::new) + .filter(task -> !shouldExcludeTask(task, taskIdsToExclude)) + .collect(Collectors.toList()); + + mNumHiddenTasks = Math.max(0, tasks.size() - desktopTasks.size()); + } else if (!desktopTasks.isEmpty()) { + mTasks = desktopTasks.get(0).getTasks().stream() .map(SingleTask::new) .filter(task -> !shouldExcludeTask(task, taskIdsToExclude)) .collect(Collectors.toList()); @@ -289,14 +306,6 @@ public final class KeyboardQuickSwitchController implements } } - @Nullable - private DesktopTask findDesktopTask(List tasks) { - return (DesktopTask) tasks.stream() - .filter(t -> t instanceof DesktopTask) - .findFirst() - .orElse(null); - } - void closeQuickSwitchView() { closeQuickSwitchView(true); }