Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +15 −12 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; Loading Loading @@ -264,10 +265,10 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll /** * Show apps on desktop */ void showDesktopApps() { void showDesktopApps(int displayId) { // Bring apps to front, ignoring their visibility status to always ensure they are on top. WindowContainerTransaction wct = new WindowContainerTransaction(); bringDesktopAppsToFront(wct); bringDesktopAppsToFront(displayId, wct); if (!wct.isEmpty()) { if (Transitions.ENABLE_SHELL_TRANSITIONS) { Loading @@ -280,12 +281,12 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll } /** Get number of tasks that are marked as visible */ int getVisibleTaskCount() { return mDesktopModeTaskRepository.getVisibleTaskCount(); int getVisibleTaskCount(int displayId) { return mDesktopModeTaskRepository.getVisibleTaskCount(displayId); } private void bringDesktopAppsToFront(WindowContainerTransaction wct) { final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(); private void bringDesktopAppsToFront(int displayId, WindowContainerTransaction wct) { final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(displayId); ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size()); final List<RunningTaskInfo> taskInfos = new ArrayList<>(); Loading Loading @@ -386,6 +387,7 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { RunningTaskInfo triggerTask = request.getTriggerTask(); // Only do anything if we are in desktop mode and opening/moving-to-front a task/app in // freeform if (!DesktopModeStatus.isActive(mContext)) { Loading @@ -399,16 +401,15 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll WindowManager.transitTypeToString(request.getType())); return null; } if (request.getTriggerTask() == null || request.getTriggerTask().getWindowingMode() != WINDOWING_MODE_FREEFORM) { if (triggerTask == null || triggerTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "skip shell transition request: not freeform task"); return null; } ProtoLog.d(WM_SHELL_DESKTOP_MODE, "handle shell transition request: %s", request); WindowContainerTransaction wct = new WindowContainerTransaction(); bringDesktopAppsToFront(wct); wct.reorder(request.getTriggerTask().token, true /* onTop */); bringDesktopAppsToFront(triggerTask.displayId, wct); wct.reorder(triggerTask.token, true /* onTop */); return wct; } Loading Loading @@ -493,16 +494,18 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll mController = null; } // TODO(b/278084491): pass in display id public void showDesktopApps() { executeRemoteCallWithTaskPermission(mController, "showDesktopApps", DesktopModeController::showDesktopApps); controller -> controller.showDesktopApps(DEFAULT_DISPLAY)); } // TODO(b/278084491): pass in display id @Override public int getVisibleTaskCount() throws RemoteException { int[] result = new int[1]; executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount", controller -> result[0] = controller.getVisibleTaskCount(), controller -> result[0] = controller.getVisibleTaskCount(DEFAULT_DISPLAY), true /* blocking */ ); return result[0]; Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +105 −44 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import android.graphics.Region import android.util.ArrayMap import android.util.ArraySet import android.util.SparseArray import androidx.core.util.forEach import androidx.core.util.keyIterator import androidx.core.util.valueIterator import java.util.concurrent.Executor import java.util.function.Consumer Loading @@ -29,14 +31,18 @@ import java.util.function.Consumer */ class DesktopModeTaskRepository { /** Task data that is tracked per display */ private data class DisplayData( /** * Set of task ids that are marked as active in desktop mode. * Active tasks in desktop mode are freeform tasks that are visible or have been visible after * desktop mode was activated. * Task gets removed from this list when it vanishes. Or when desktop mode is turned off. * Set of task ids that are marked as active in desktop mode. Active tasks in desktop mode * are freeform tasks that are visible or have been visible after desktop mode was * activated. Task gets removed from this list when it vanishes. Or when desktop mode is * turned off. */ private val activeTasks = ArraySet<Int>() private val visibleTasks = ArraySet<Int>() val activeTasks: ArraySet<Int> = ArraySet(), val visibleTasks: ArraySet<Int> = ArraySet(), ) // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0). private val freeformTasksInZOrder = mutableListOf<Int>() private val activeTasksListeners = ArraySet<ActiveTasksListener>() Loading @@ -47,9 +53,22 @@ class DesktopModeTaskRepository { private var desktopGestureExclusionListener: Consumer<Region>? = null private var desktopGestureExclusionExecutor: Executor? = null private val displayData = object : SparseArray<DisplayData>() { /** * Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository. * Get the [DisplayData] associated with this [displayId] * * Creates a new instance if one does not exist */ fun getOrCreate(displayId: Int): DisplayData { if (!contains(displayId)) { put(displayId, DisplayData()) } return get(displayId) } } /** Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository. */ fun addActiveTaskListener(activeTasksListener: ActiveTasksListener) { activeTasksListeners.add(activeTasksListener) } Loading @@ -57,10 +76,17 @@ class DesktopModeTaskRepository { /** * Add a [VisibleTasksListener] to be notified when freeform tasks are visible or not. */ fun addVisibleTasksListener(visibleTasksListener: VisibleTasksListener, executor: Executor) { visibleTasksListeners.put(visibleTasksListener, executor) executor.execute( Runnable { visibleTasksListener.onVisibilityChanged(visibleTasks.size > 0) }) fun addVisibleTasksListener( visibleTasksListener: VisibleTasksListener, executor: Executor ) { visibleTasksListeners[visibleTasksListener] = executor displayData.keyIterator().forEach { displayId -> val visibleTasks = getVisibleTaskCount(displayId) executor.execute { visibleTasksListener.onVisibilityChanged(displayId, visibleTasks > 0) } } } /** Loading Loading @@ -100,14 +126,21 @@ class DesktopModeTaskRepository { } /** * Mark a task with given [taskId] as active. * Mark a task with given [taskId] as active on given [displayId] * * @return `true` if the task was not active * @return `true` if the task was not active on given [displayId] */ fun addActiveTask(taskId: Int): Boolean { val added = activeTasks.add(taskId) fun addActiveTask(displayId: Int, taskId: Int): Boolean { // Check if task is active on another display, if so, remove it displayData.forEach { id, data -> if (id != displayId && data.activeTasks.remove(taskId)) { activeTasksListeners.onEach { it.onActiveTasksChanged(id) } } } val added = displayData.getOrCreate(displayId).activeTasks.add(taskId) if (added) { activeTasksListeners.onEach { it.onActiveTasksChanged() } activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) } } return added } Loading @@ -118,65 +151,93 @@ class DesktopModeTaskRepository { * @return `true` if the task was active */ fun removeActiveTask(taskId: Int): Boolean { val removed = activeTasks.remove(taskId) if (removed) { activeTasksListeners.onEach { it.onActiveTasksChanged() } var result = false displayData.forEach { displayId, data -> if (data.activeTasks.remove(taskId)) { activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) } result = true } return removed } return result } /** * Check if a task with the given [taskId] was marked as an active task */ fun isActiveTask(taskId: Int): Boolean { return activeTasks.contains(taskId) return displayData.valueIterator().asSequence().any { data -> data.activeTasks.contains(taskId) } } /** * Whether a task is visible. */ fun isVisibleTask(taskId: Int): Boolean { return visibleTasks.contains(taskId) return displayData.valueIterator().asSequence().any { data -> data.visibleTasks.contains(taskId) } } /** * Get a set of the active tasks * Get a set of the active tasks for given [displayId] */ fun getActiveTasks(): ArraySet<Int> { return ArraySet(activeTasks) fun getActiveTasks(displayId: Int): ArraySet<Int> { return ArraySet(displayData[displayId]?.activeTasks) } /** * Get a list of freeform tasks, ordered from top-bottom (top at index 0). */ // TODO(b/278084491): pass in display id fun getFreeformTasksInZOrder(): List<Int> { return freeformTasksInZOrder } /** * Updates whether a freeform task with this id is visible or not and notifies listeners. * * If the task was visible on a different display with a different displayId, it is removed from * the set of visible tasks on that display. Listeners will be notified. */ fun updateVisibleFreeformTasks(taskId: Int, visible: Boolean) { val prevCount: Int = visibleTasks.size fun updateVisibleFreeformTasks(displayId: Int, taskId: Int, visible: Boolean) { if (visible) { // Task is visible. Check if we need to remove it from any other display. val otherDisplays = displayData.keyIterator().asSequence().filter { it != displayId } for (otherDisplayId in otherDisplays) { if (displayData[otherDisplayId].visibleTasks.remove(taskId)) { // Task removed from other display, check if we should notify listeners if (displayData[otherDisplayId].visibleTasks.isEmpty()) { notifyVisibleTaskListeners(otherDisplayId, hasVisibleFreeformTasks = false) } } } } val prevCount = getVisibleTaskCount(displayId) if (visible) { visibleTasks.add(taskId) displayData.getOrCreate(displayId).visibleTasks.add(taskId) } else { visibleTasks.remove(taskId) displayData[displayId]?.visibleTasks?.remove(taskId) } val newCount = getVisibleTaskCount(displayId) // Check if count changed and if there was no tasks or this is the first task if (prevCount != newCount && (prevCount == 0 || newCount == 0)) { notifyVisibleTaskListeners(displayId, newCount > 0) } if (prevCount == 0 && visibleTasks.size == 1 || prevCount > 0 && visibleTasks.size == 0) { for ((listener, executor) in visibleTasksListeners) { executor.execute( Runnable { listener.onVisibilityChanged(visibleTasks.size > 0) }) } private fun notifyVisibleTaskListeners(displayId: Int, hasVisibleFreeformTasks: Boolean) { visibleTasksListeners.forEach { (listener, executor) -> executor.execute { listener.onVisibilityChanged(displayId, hasVisibleFreeformTasks) } } } /** * Get number of tasks that are marked as visible * Get number of tasks that are marked as visible on given [displayId] */ fun getVisibleTaskCount(): Int { return visibleTasks.size fun getVisibleTaskCount(displayId: Int): Int { return displayData[displayId]?.visibleTasks?.size ?: 0 } /** Loading Loading @@ -226,7 +287,7 @@ class DesktopModeTaskRepository { * Called when the active tasks change in desktop mode. */ @JvmDefault fun onActiveTasksChanged() {} fun onActiveTasksChanged(displayId: Int) {} } /** Loading @@ -237,6 +298,6 @@ class DesktopModeTaskRepository { * Called when the desktop starts or stops showing freeform tasks. */ @JvmDefault fun onVisibilityChanged(hasVisibleFreeformTasks: Boolean) {} fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {} } } libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +23 −20 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.graphics.Rect import android.graphics.Region import android.os.IBinder import android.os.SystemProperties import android.view.Display.DEFAULT_DISPLAY import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_NONE Loading Loading @@ -97,10 +98,11 @@ class DesktopTasksController( } /** Show all tasks, that are part of the desktop, on top of launcher */ fun showDesktopApps() { fun showDesktopApps(displayId: Int) { ProtoLog.v(WM_SHELL_DESKTOP_MODE, "showDesktopApps") val wct = WindowContainerTransaction() bringDesktopAppsToFront(wct) // TODO(b/278084491): pass in display id bringDesktopAppsToFront(displayId, wct) // Execute transaction if there are pending operations if (!wct.isEmpty) { Loading @@ -114,8 +116,8 @@ class DesktopTasksController( } /** Get number of tasks that are marked as visible */ fun getVisibleTaskCount(): Int { return desktopModeTaskRepository.getVisibleTaskCount() fun getVisibleTaskCount(displayId: Int): Int { return desktopModeTaskRepository.getVisibleTaskCount(displayId) } /** Move a task with given `taskId` to desktop */ Loading @@ -129,7 +131,7 @@ class DesktopTasksController( val wct = WindowContainerTransaction() // Bring other apps to front first bringDesktopAppsToFront(wct) bringDesktopAppsToFront(task.displayId, wct) addMoveToDesktopChanges(wct, task.token) if (Transitions.ENABLE_SHELL_TRANSITIONS) { transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) Loading Loading @@ -165,7 +167,7 @@ class DesktopTasksController( freeformBounds: Rect ) { val wct = WindowContainerTransaction() bringDesktopAppsToFront(wct) bringDesktopAppsToFront(taskInfo.displayId, wct) addMoveToDesktopChanges(wct, taskInfo.getToken()) wct.setBounds(taskInfo.token, freeformBounds) Loading Loading @@ -244,9 +246,9 @@ class DesktopTasksController( ?: WINDOWING_MODE_UNDEFINED } private fun bringDesktopAppsToFront(wct: WindowContainerTransaction) { private fun bringDesktopAppsToFront(displayId: Int, wct: WindowContainerTransaction) { ProtoLog.v(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront") val activeTasks = desktopModeTaskRepository.getActiveTasks() val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId) // First move home to front and then other tasks on top of it moveHomeTaskToFront(wct) Loading Loading @@ -290,18 +292,17 @@ class DesktopTasksController( request: TransitionRequestInfo ): WindowContainerTransaction? { // Check if we should skip handling this transition val task: RunningTaskInfo? = request.triggerTask val shouldHandleRequest = when { // Only handle open or to front transitions request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false // Only handle when it is a task transition task == null -> false request.triggerTask == null -> false // Only handle standard type tasks task.activityType != ACTIVITY_TYPE_STANDARD -> false request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> false // Only handle fullscreen or freeform tasks task.windowingMode != WINDOWING_MODE_FULLSCREEN && task.windowingMode != WINDOWING_MODE_FREEFORM -> false request.triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN && request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> false // Otherwise process it else -> true } Loading @@ -310,10 +311,11 @@ class DesktopTasksController( return null } val activeTasks = desktopModeTaskRepository.getActiveTasks() val task: RunningTaskInfo = request.triggerTask val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) // Check if we should switch a fullscreen task to freeform if (task?.windowingMode == WINDOWING_MODE_FULLSCREEN) { if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) { // If there are any visible desktop tasks, switch the task to freeform if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) { ProtoLog.d( Loading @@ -329,7 +331,7 @@ class DesktopTasksController( } // CHeck if we should switch a freeform task to fullscreen if (task?.windowingMode == WINDOWING_MODE_FREEFORM) { if (task.windowingMode == WINDOWING_MODE_FREEFORM) { // If no visible desktop tasks, switch this task to freeform as the transition came // outside of this controller if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) { Loading Loading @@ -559,20 +561,21 @@ class DesktopTasksController( controller = null } // TODO(b/278084491): pass in display id override fun showDesktopApps() { ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "showDesktopApps", Consumer(DesktopTasksController::showDesktopApps) ) "showDesktopApps" ) { c -> c.showDesktopApps(DEFAULT_DISPLAY) } } // TODO(b/278084491): pass in display id override fun getVisibleTaskCount(): Int { val result = IntArray(1) ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "getVisibleTaskCount", { controller -> result[0] = controller.getVisibleTaskCount() }, { controller -> result[0] = controller.getVisibleTaskCount(DEFAULT_DISPLAY) }, true /* blocking */ ) return result[0] Loading libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +7 −5 Original line number Diff line number Diff line Loading @@ -94,11 +94,12 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, mDesktopModeTaskRepository.ifPresent(repository -> { repository.addOrMoveFreeformTaskToTop(taskInfo.taskId); if (taskInfo.isVisible) { if (repository.addActiveTask(taskInfo.taskId)) { if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Adding active freeform task: #%d", taskInfo.taskId); } repository.updateVisibleFreeformTasks(taskInfo.taskId, true); repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, true); } }); } Loading @@ -117,7 +118,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Removing active freeform task: #%d", taskInfo.taskId); } repository.updateVisibleFreeformTasks(taskInfo.taskId, false); repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, false); }); } Loading @@ -137,12 +138,13 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, if (DesktopModeStatus.isAnyEnabled()) { mDesktopModeTaskRepository.ifPresent(repository -> { if (taskInfo.isVisible) { if (repository.addActiveTask(taskInfo.taskId)) { if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Adding active freeform task: #%d", taskInfo.taskId); } } repository.updateVisibleFreeformTasks(taskInfo.taskId, taskInfo.isVisible); repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible); }); } } Loading libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +1 −1 Original line number Diff line number Diff line Loading @@ -245,7 +245,7 @@ public class RecentTasksController implements TaskStackListenerCallback, } @Override public void onActiveTasksChanged() { public void onActiveTasksChanged(int displayId) { notifyRecentTasksChanged(); } Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +15 −12 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; Loading Loading @@ -264,10 +265,10 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll /** * Show apps on desktop */ void showDesktopApps() { void showDesktopApps(int displayId) { // Bring apps to front, ignoring their visibility status to always ensure they are on top. WindowContainerTransaction wct = new WindowContainerTransaction(); bringDesktopAppsToFront(wct); bringDesktopAppsToFront(displayId, wct); if (!wct.isEmpty()) { if (Transitions.ENABLE_SHELL_TRANSITIONS) { Loading @@ -280,12 +281,12 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll } /** Get number of tasks that are marked as visible */ int getVisibleTaskCount() { return mDesktopModeTaskRepository.getVisibleTaskCount(); int getVisibleTaskCount(int displayId) { return mDesktopModeTaskRepository.getVisibleTaskCount(displayId); } private void bringDesktopAppsToFront(WindowContainerTransaction wct) { final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(); private void bringDesktopAppsToFront(int displayId, WindowContainerTransaction wct) { final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(displayId); ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size()); final List<RunningTaskInfo> taskInfos = new ArrayList<>(); Loading Loading @@ -386,6 +387,7 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { RunningTaskInfo triggerTask = request.getTriggerTask(); // Only do anything if we are in desktop mode and opening/moving-to-front a task/app in // freeform if (!DesktopModeStatus.isActive(mContext)) { Loading @@ -399,16 +401,15 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll WindowManager.transitTypeToString(request.getType())); return null; } if (request.getTriggerTask() == null || request.getTriggerTask().getWindowingMode() != WINDOWING_MODE_FREEFORM) { if (triggerTask == null || triggerTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) { ProtoLog.d(WM_SHELL_DESKTOP_MODE, "skip shell transition request: not freeform task"); return null; } ProtoLog.d(WM_SHELL_DESKTOP_MODE, "handle shell transition request: %s", request); WindowContainerTransaction wct = new WindowContainerTransaction(); bringDesktopAppsToFront(wct); wct.reorder(request.getTriggerTask().token, true /* onTop */); bringDesktopAppsToFront(triggerTask.displayId, wct); wct.reorder(triggerTask.token, true /* onTop */); return wct; } Loading Loading @@ -493,16 +494,18 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll mController = null; } // TODO(b/278084491): pass in display id public void showDesktopApps() { executeRemoteCallWithTaskPermission(mController, "showDesktopApps", DesktopModeController::showDesktopApps); controller -> controller.showDesktopApps(DEFAULT_DISPLAY)); } // TODO(b/278084491): pass in display id @Override public int getVisibleTaskCount() throws RemoteException { int[] result = new int[1]; executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount", controller -> result[0] = controller.getVisibleTaskCount(), controller -> result[0] = controller.getVisibleTaskCount(DEFAULT_DISPLAY), true /* blocking */ ); return result[0]; Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +105 −44 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import android.graphics.Region import android.util.ArrayMap import android.util.ArraySet import android.util.SparseArray import androidx.core.util.forEach import androidx.core.util.keyIterator import androidx.core.util.valueIterator import java.util.concurrent.Executor import java.util.function.Consumer Loading @@ -29,14 +31,18 @@ import java.util.function.Consumer */ class DesktopModeTaskRepository { /** Task data that is tracked per display */ private data class DisplayData( /** * Set of task ids that are marked as active in desktop mode. * Active tasks in desktop mode are freeform tasks that are visible or have been visible after * desktop mode was activated. * Task gets removed from this list when it vanishes. Or when desktop mode is turned off. * Set of task ids that are marked as active in desktop mode. Active tasks in desktop mode * are freeform tasks that are visible or have been visible after desktop mode was * activated. Task gets removed from this list when it vanishes. Or when desktop mode is * turned off. */ private val activeTasks = ArraySet<Int>() private val visibleTasks = ArraySet<Int>() val activeTasks: ArraySet<Int> = ArraySet(), val visibleTasks: ArraySet<Int> = ArraySet(), ) // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0). private val freeformTasksInZOrder = mutableListOf<Int>() private val activeTasksListeners = ArraySet<ActiveTasksListener>() Loading @@ -47,9 +53,22 @@ class DesktopModeTaskRepository { private var desktopGestureExclusionListener: Consumer<Region>? = null private var desktopGestureExclusionExecutor: Executor? = null private val displayData = object : SparseArray<DisplayData>() { /** * Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository. * Get the [DisplayData] associated with this [displayId] * * Creates a new instance if one does not exist */ fun getOrCreate(displayId: Int): DisplayData { if (!contains(displayId)) { put(displayId, DisplayData()) } return get(displayId) } } /** Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository. */ fun addActiveTaskListener(activeTasksListener: ActiveTasksListener) { activeTasksListeners.add(activeTasksListener) } Loading @@ -57,10 +76,17 @@ class DesktopModeTaskRepository { /** * Add a [VisibleTasksListener] to be notified when freeform tasks are visible or not. */ fun addVisibleTasksListener(visibleTasksListener: VisibleTasksListener, executor: Executor) { visibleTasksListeners.put(visibleTasksListener, executor) executor.execute( Runnable { visibleTasksListener.onVisibilityChanged(visibleTasks.size > 0) }) fun addVisibleTasksListener( visibleTasksListener: VisibleTasksListener, executor: Executor ) { visibleTasksListeners[visibleTasksListener] = executor displayData.keyIterator().forEach { displayId -> val visibleTasks = getVisibleTaskCount(displayId) executor.execute { visibleTasksListener.onVisibilityChanged(displayId, visibleTasks > 0) } } } /** Loading Loading @@ -100,14 +126,21 @@ class DesktopModeTaskRepository { } /** * Mark a task with given [taskId] as active. * Mark a task with given [taskId] as active on given [displayId] * * @return `true` if the task was not active * @return `true` if the task was not active on given [displayId] */ fun addActiveTask(taskId: Int): Boolean { val added = activeTasks.add(taskId) fun addActiveTask(displayId: Int, taskId: Int): Boolean { // Check if task is active on another display, if so, remove it displayData.forEach { id, data -> if (id != displayId && data.activeTasks.remove(taskId)) { activeTasksListeners.onEach { it.onActiveTasksChanged(id) } } } val added = displayData.getOrCreate(displayId).activeTasks.add(taskId) if (added) { activeTasksListeners.onEach { it.onActiveTasksChanged() } activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) } } return added } Loading @@ -118,65 +151,93 @@ class DesktopModeTaskRepository { * @return `true` if the task was active */ fun removeActiveTask(taskId: Int): Boolean { val removed = activeTasks.remove(taskId) if (removed) { activeTasksListeners.onEach { it.onActiveTasksChanged() } var result = false displayData.forEach { displayId, data -> if (data.activeTasks.remove(taskId)) { activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) } result = true } return removed } return result } /** * Check if a task with the given [taskId] was marked as an active task */ fun isActiveTask(taskId: Int): Boolean { return activeTasks.contains(taskId) return displayData.valueIterator().asSequence().any { data -> data.activeTasks.contains(taskId) } } /** * Whether a task is visible. */ fun isVisibleTask(taskId: Int): Boolean { return visibleTasks.contains(taskId) return displayData.valueIterator().asSequence().any { data -> data.visibleTasks.contains(taskId) } } /** * Get a set of the active tasks * Get a set of the active tasks for given [displayId] */ fun getActiveTasks(): ArraySet<Int> { return ArraySet(activeTasks) fun getActiveTasks(displayId: Int): ArraySet<Int> { return ArraySet(displayData[displayId]?.activeTasks) } /** * Get a list of freeform tasks, ordered from top-bottom (top at index 0). */ // TODO(b/278084491): pass in display id fun getFreeformTasksInZOrder(): List<Int> { return freeformTasksInZOrder } /** * Updates whether a freeform task with this id is visible or not and notifies listeners. * * If the task was visible on a different display with a different displayId, it is removed from * the set of visible tasks on that display. Listeners will be notified. */ fun updateVisibleFreeformTasks(taskId: Int, visible: Boolean) { val prevCount: Int = visibleTasks.size fun updateVisibleFreeformTasks(displayId: Int, taskId: Int, visible: Boolean) { if (visible) { // Task is visible. Check if we need to remove it from any other display. val otherDisplays = displayData.keyIterator().asSequence().filter { it != displayId } for (otherDisplayId in otherDisplays) { if (displayData[otherDisplayId].visibleTasks.remove(taskId)) { // Task removed from other display, check if we should notify listeners if (displayData[otherDisplayId].visibleTasks.isEmpty()) { notifyVisibleTaskListeners(otherDisplayId, hasVisibleFreeformTasks = false) } } } } val prevCount = getVisibleTaskCount(displayId) if (visible) { visibleTasks.add(taskId) displayData.getOrCreate(displayId).visibleTasks.add(taskId) } else { visibleTasks.remove(taskId) displayData[displayId]?.visibleTasks?.remove(taskId) } val newCount = getVisibleTaskCount(displayId) // Check if count changed and if there was no tasks or this is the first task if (prevCount != newCount && (prevCount == 0 || newCount == 0)) { notifyVisibleTaskListeners(displayId, newCount > 0) } if (prevCount == 0 && visibleTasks.size == 1 || prevCount > 0 && visibleTasks.size == 0) { for ((listener, executor) in visibleTasksListeners) { executor.execute( Runnable { listener.onVisibilityChanged(visibleTasks.size > 0) }) } private fun notifyVisibleTaskListeners(displayId: Int, hasVisibleFreeformTasks: Boolean) { visibleTasksListeners.forEach { (listener, executor) -> executor.execute { listener.onVisibilityChanged(displayId, hasVisibleFreeformTasks) } } } /** * Get number of tasks that are marked as visible * Get number of tasks that are marked as visible on given [displayId] */ fun getVisibleTaskCount(): Int { return visibleTasks.size fun getVisibleTaskCount(displayId: Int): Int { return displayData[displayId]?.visibleTasks?.size ?: 0 } /** Loading Loading @@ -226,7 +287,7 @@ class DesktopModeTaskRepository { * Called when the active tasks change in desktop mode. */ @JvmDefault fun onActiveTasksChanged() {} fun onActiveTasksChanged(displayId: Int) {} } /** Loading @@ -237,6 +298,6 @@ class DesktopModeTaskRepository { * Called when the desktop starts or stops showing freeform tasks. */ @JvmDefault fun onVisibilityChanged(hasVisibleFreeformTasks: Boolean) {} fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {} } }
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +23 −20 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.graphics.Rect import android.graphics.Region import android.os.IBinder import android.os.SystemProperties import android.view.Display.DEFAULT_DISPLAY import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_NONE Loading Loading @@ -97,10 +98,11 @@ class DesktopTasksController( } /** Show all tasks, that are part of the desktop, on top of launcher */ fun showDesktopApps() { fun showDesktopApps(displayId: Int) { ProtoLog.v(WM_SHELL_DESKTOP_MODE, "showDesktopApps") val wct = WindowContainerTransaction() bringDesktopAppsToFront(wct) // TODO(b/278084491): pass in display id bringDesktopAppsToFront(displayId, wct) // Execute transaction if there are pending operations if (!wct.isEmpty) { Loading @@ -114,8 +116,8 @@ class DesktopTasksController( } /** Get number of tasks that are marked as visible */ fun getVisibleTaskCount(): Int { return desktopModeTaskRepository.getVisibleTaskCount() fun getVisibleTaskCount(displayId: Int): Int { return desktopModeTaskRepository.getVisibleTaskCount(displayId) } /** Move a task with given `taskId` to desktop */ Loading @@ -129,7 +131,7 @@ class DesktopTasksController( val wct = WindowContainerTransaction() // Bring other apps to front first bringDesktopAppsToFront(wct) bringDesktopAppsToFront(task.displayId, wct) addMoveToDesktopChanges(wct, task.token) if (Transitions.ENABLE_SHELL_TRANSITIONS) { transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) Loading Loading @@ -165,7 +167,7 @@ class DesktopTasksController( freeformBounds: Rect ) { val wct = WindowContainerTransaction() bringDesktopAppsToFront(wct) bringDesktopAppsToFront(taskInfo.displayId, wct) addMoveToDesktopChanges(wct, taskInfo.getToken()) wct.setBounds(taskInfo.token, freeformBounds) Loading Loading @@ -244,9 +246,9 @@ class DesktopTasksController( ?: WINDOWING_MODE_UNDEFINED } private fun bringDesktopAppsToFront(wct: WindowContainerTransaction) { private fun bringDesktopAppsToFront(displayId: Int, wct: WindowContainerTransaction) { ProtoLog.v(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront") val activeTasks = desktopModeTaskRepository.getActiveTasks() val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId) // First move home to front and then other tasks on top of it moveHomeTaskToFront(wct) Loading Loading @@ -290,18 +292,17 @@ class DesktopTasksController( request: TransitionRequestInfo ): WindowContainerTransaction? { // Check if we should skip handling this transition val task: RunningTaskInfo? = request.triggerTask val shouldHandleRequest = when { // Only handle open or to front transitions request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false // Only handle when it is a task transition task == null -> false request.triggerTask == null -> false // Only handle standard type tasks task.activityType != ACTIVITY_TYPE_STANDARD -> false request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> false // Only handle fullscreen or freeform tasks task.windowingMode != WINDOWING_MODE_FULLSCREEN && task.windowingMode != WINDOWING_MODE_FREEFORM -> false request.triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN && request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> false // Otherwise process it else -> true } Loading @@ -310,10 +311,11 @@ class DesktopTasksController( return null } val activeTasks = desktopModeTaskRepository.getActiveTasks() val task: RunningTaskInfo = request.triggerTask val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) // Check if we should switch a fullscreen task to freeform if (task?.windowingMode == WINDOWING_MODE_FULLSCREEN) { if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) { // If there are any visible desktop tasks, switch the task to freeform if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) { ProtoLog.d( Loading @@ -329,7 +331,7 @@ class DesktopTasksController( } // CHeck if we should switch a freeform task to fullscreen if (task?.windowingMode == WINDOWING_MODE_FREEFORM) { if (task.windowingMode == WINDOWING_MODE_FREEFORM) { // If no visible desktop tasks, switch this task to freeform as the transition came // outside of this controller if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) { Loading Loading @@ -559,20 +561,21 @@ class DesktopTasksController( controller = null } // TODO(b/278084491): pass in display id override fun showDesktopApps() { ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "showDesktopApps", Consumer(DesktopTasksController::showDesktopApps) ) "showDesktopApps" ) { c -> c.showDesktopApps(DEFAULT_DISPLAY) } } // TODO(b/278084491): pass in display id override fun getVisibleTaskCount(): Int { val result = IntArray(1) ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "getVisibleTaskCount", { controller -> result[0] = controller.getVisibleTaskCount() }, { controller -> result[0] = controller.getVisibleTaskCount(DEFAULT_DISPLAY) }, true /* blocking */ ) return result[0] Loading
libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +7 −5 Original line number Diff line number Diff line Loading @@ -94,11 +94,12 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, mDesktopModeTaskRepository.ifPresent(repository -> { repository.addOrMoveFreeformTaskToTop(taskInfo.taskId); if (taskInfo.isVisible) { if (repository.addActiveTask(taskInfo.taskId)) { if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Adding active freeform task: #%d", taskInfo.taskId); } repository.updateVisibleFreeformTasks(taskInfo.taskId, true); repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, true); } }); } Loading @@ -117,7 +118,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Removing active freeform task: #%d", taskInfo.taskId); } repository.updateVisibleFreeformTasks(taskInfo.taskId, false); repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, false); }); } Loading @@ -137,12 +138,13 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, if (DesktopModeStatus.isAnyEnabled()) { mDesktopModeTaskRepository.ifPresent(repository -> { if (taskInfo.isVisible) { if (repository.addActiveTask(taskInfo.taskId)) { if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Adding active freeform task: #%d", taskInfo.taskId); } } repository.updateVisibleFreeformTasks(taskInfo.taskId, taskInfo.isVisible); repository.updateVisibleFreeformTasks(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible); }); } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +1 −1 Original line number Diff line number Diff line Loading @@ -245,7 +245,7 @@ public class RecentTasksController implements TaskStackListenerCallback, } @Override public void onActiveTasksChanged() { public void onActiveTasksChanged(int displayId) { notifyRecentTasksChanged(); } Loading