Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +10 −0 Original line number Diff line number Diff line Loading @@ -508,5 +508,15 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll ); return result[0]; } @Override public void stashDesktopApps(int displayId) throws RemoteException { // Stashing of desktop apps not needed. Apps always launch on desktop } @Override public void setTaskListener(IDesktopTaskListener listener) throws RemoteException { // TODO(b/261234402): move visibility from sysui state to listener } } } libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +34 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ class DesktopModeTaskRepository { */ val activeTasks: ArraySet<Int> = ArraySet(), val visibleTasks: ArraySet<Int> = ArraySet(), var stashed: Boolean = false ) // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0). Loading Loading @@ -311,6 +312,33 @@ class DesktopModeTaskRepository { } } /** * Update stashed status on display with id [displayId] */ fun setStashed(displayId: Int, stashed: Boolean) { val data = displayData.getOrCreate(displayId) val oldValue = data.stashed data.stashed = stashed if (oldValue != stashed) { KtProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: mark stashed=%b displayId=%d", stashed, displayId ) visibleTasksListeners.forEach { (listener, executor) -> executor.execute { listener.onStashedChanged(displayId, stashed) } } } } /** * Check if display with id [displayId] has desktop tasks stashed */ fun isStashed(displayId: Int): Boolean { return displayData[displayId]?.stashed ?: false } /** * Defines interface for classes that can listen to changes for active tasks in desktop mode. */ Loading @@ -331,5 +359,11 @@ class DesktopModeTaskRepository { */ @JvmDefault fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {} /** * Called when the desktop stashed status changes. */ @JvmDefault fun onStashedChanged(displayId: Int, stashed: Boolean) {} } } libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +119 −24 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import com.android.wm.shell.common.ExecutorUtils import com.android.wm.shell.common.ExternalInterfaceBinder import com.android.wm.shell.common.RemoteCallable import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SingleInstanceRemoteListener import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.annotations.ExternalThread import com.android.wm.shell.common.annotations.ShellMainThread Loading Loading @@ -118,6 +119,18 @@ class DesktopTasksController( } } /** * Stash desktop tasks on display with id [displayId]. * * When desktop tasks are stashed, launcher home screen icons are fully visible. New apps * launched in this state will be added to the desktop. Existing desktop tasks will be brought * back to front during the launch. */ fun stashDesktopApps(displayId: Int) { KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: stashDesktopApps") desktopModeTaskRepository.setStashed(displayId, true) } /** Get number of tasks that are marked as visible */ fun getVisibleTaskCount(displayId: Int): Int { return desktopModeTaskRepository.getVisibleTaskCount(displayId) Loading Loading @@ -397,6 +410,11 @@ class DesktopTasksController( transition: IBinder, request: TransitionRequestInfo ): WindowContainerTransaction? { KtProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleRequest request=%s", request ) // Check if we should skip handling this transition val shouldHandleRequest = when { Loading @@ -418,43 +436,63 @@ class DesktopTasksController( } val task: RunningTaskInfo = request.triggerTask val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) // Check if we should switch a fullscreen task to freeform if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) { // If there are any visible desktop tasks, switch the task to freeform if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) { return when { // If display has tasks stashed, handle as stashed launch desktopModeTaskRepository.isStashed(task.displayId) -> handleStashedTaskLaunch(task) // Check if fullscreen task should be updated task.windowingMode == WINDOWING_MODE_FULLSCREEN -> handleFullscreenTaskLaunch(task) // Check if freeform task should be updated task.windowingMode == WINDOWING_MODE_FREEFORM -> handleFreeformTaskLaunch(task) else -> null } } private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? { val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) { KtProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: switch fullscreen task to freeform on transition" + "DesktopTasksController: switch freeform task to fullscreen oon transition" + " taskId=%d", task.taskId ) return WindowContainerTransaction().also { wct -> addMoveToDesktopChanges(wct, task.token) addMoveToFullscreenChanges(wct, task.token) } } return null } // CHeck if we should switch a freeform task to fullscreen if (task.windowingMode == WINDOWING_MODE_FREEFORM) { // If no visible desktop tasks, switch this task to freeform as the transition came // outside of this controller if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) { private fun handleFullscreenTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? { val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) { KtProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: switch freeform task to fullscreen oon transition" + "DesktopTasksController: switch fullscreen task to freeform on transition" + " taskId=%d", task.taskId ) return WindowContainerTransaction().also { wct -> addMoveToFullscreenChanges(wct, task.token) } addMoveToDesktopChanges(wct, task.token) } } return null } private fun handleStashedTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction { KtProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: launch apps with stashed on transition taskId=%d", task.taskId ) val wct = WindowContainerTransaction() bringDesktopAppsToFront(task.displayId, wct) addMoveToDesktopChanges(wct, task.token) desktopModeTaskRepository.setStashed(task.displayId, false) return wct } private fun addMoveToDesktopChanges( wct: WindowContainerTransaction, token: WindowContainerToken Loading Loading @@ -658,8 +696,46 @@ class DesktopTasksController( @BinderThread private class IDesktopModeImpl(private var controller: DesktopTasksController?) : IDesktopMode.Stub(), ExternalInterfaceBinder { private lateinit var remoteListener: SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener> private val listener: VisibleTasksListener = object : VisibleTasksListener { override fun onVisibilityChanged(displayId: Int, visible: Boolean) { // TODO(b/261234402): move visibility from sysui state to listener remoteListener.call { l -> l.onVisibilityChanged(displayId, visible) } } override fun onStashedChanged(displayId: Int, stashed: Boolean) { KtProtoLog.v( WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: onStashedChanged stashed=%b display=%d", stashed, displayId ) remoteListener.call { l -> l.onStashedChanged(displayId, stashed) } } } init { remoteListener = SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>( controller, { c -> c.desktopModeTaskRepository.addVisibleTasksListener( listener, c.mainExecutor ) }, { c -> c.desktopModeTaskRepository.removeVisibleTasksListener(listener) } ) } /** Invalidates this instance, preventing future calls from updating the controller. */ override fun invalidate() { remoteListener.unregister() controller = null } Loading @@ -670,6 +746,13 @@ class DesktopTasksController( ) { c -> c.showDesktopApps(displayId) } } override fun stashDesktopApps(displayId: Int) { ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "stashDesktopApps" ) { c -> c.stashDesktopApps(displayId) } } override fun getVisibleTaskCount(displayId: Int): Int { val result = IntArray(1) ExecutorUtils.executeRemoteCallWithTaskPermission( Loading @@ -680,6 +763,18 @@ class DesktopTasksController( ) return result[0] } override fun setTaskListener(listener: IDesktopTaskListener?) { KtProtoLog.v( WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: set task listener=%s", listener ?: "null" ) ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "setTaskListener" ) { _ -> listener?.let { remoteListener.register(it) } ?: remoteListener.unregister() } } } companion object { Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +8 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.wm.shell.desktopmode; import com.android.wm.shell.desktopmode.IDesktopTaskListener; /** * Interface that is exposed to remote callers to manipulate desktop mode features. */ Loading @@ -24,6 +26,12 @@ interface IDesktopMode { /** Show apps on the desktop on the given display */ void showDesktopApps(int displayId); /** Stash apps on the desktop to allow launching another app from home screen */ void stashDesktopApps(int displayId); /** Get count of visible desktop tasks on the given display */ int getVisibleTaskCount(int displayId); /** Set listener that will receive callbacks about updates to desktop tasks */ oneway void setTaskListener(IDesktopTaskListener listener); } No newline at end of file libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl 0 → 100644 +30 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.desktopmode; /** * Allows external processes to register a listener in WMShell to get updates about desktop task * state. */ interface IDesktopTaskListener { /** Desktop task visibility has change. Visible if at least 1 task is visible. */ oneway void onVisibilityChanged(int displayId, boolean visible); /** Desktop task stashed status has changed. */ oneway void onStashedChanged(int displayId, boolean stashed); } No newline at end of file Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +10 −0 Original line number Diff line number Diff line Loading @@ -508,5 +508,15 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll ); return result[0]; } @Override public void stashDesktopApps(int displayId) throws RemoteException { // Stashing of desktop apps not needed. Apps always launch on desktop } @Override public void setTaskListener(IDesktopTaskListener listener) throws RemoteException { // TODO(b/261234402): move visibility from sysui state to listener } } }
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +34 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ class DesktopModeTaskRepository { */ val activeTasks: ArraySet<Int> = ArraySet(), val visibleTasks: ArraySet<Int> = ArraySet(), var stashed: Boolean = false ) // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0). Loading Loading @@ -311,6 +312,33 @@ class DesktopModeTaskRepository { } } /** * Update stashed status on display with id [displayId] */ fun setStashed(displayId: Int, stashed: Boolean) { val data = displayData.getOrCreate(displayId) val oldValue = data.stashed data.stashed = stashed if (oldValue != stashed) { KtProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTaskRepo: mark stashed=%b displayId=%d", stashed, displayId ) visibleTasksListeners.forEach { (listener, executor) -> executor.execute { listener.onStashedChanged(displayId, stashed) } } } } /** * Check if display with id [displayId] has desktop tasks stashed */ fun isStashed(displayId: Int): Boolean { return displayData[displayId]?.stashed ?: false } /** * Defines interface for classes that can listen to changes for active tasks in desktop mode. */ Loading @@ -331,5 +359,11 @@ class DesktopModeTaskRepository { */ @JvmDefault fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {} /** * Called when the desktop stashed status changes. */ @JvmDefault fun onStashedChanged(displayId: Int, stashed: Boolean) {} } }
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +119 −24 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import com.android.wm.shell.common.ExecutorUtils import com.android.wm.shell.common.ExternalInterfaceBinder import com.android.wm.shell.common.RemoteCallable import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SingleInstanceRemoteListener import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.annotations.ExternalThread import com.android.wm.shell.common.annotations.ShellMainThread Loading Loading @@ -118,6 +119,18 @@ class DesktopTasksController( } } /** * Stash desktop tasks on display with id [displayId]. * * When desktop tasks are stashed, launcher home screen icons are fully visible. New apps * launched in this state will be added to the desktop. Existing desktop tasks will be brought * back to front during the launch. */ fun stashDesktopApps(displayId: Int) { KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: stashDesktopApps") desktopModeTaskRepository.setStashed(displayId, true) } /** Get number of tasks that are marked as visible */ fun getVisibleTaskCount(displayId: Int): Int { return desktopModeTaskRepository.getVisibleTaskCount(displayId) Loading Loading @@ -397,6 +410,11 @@ class DesktopTasksController( transition: IBinder, request: TransitionRequestInfo ): WindowContainerTransaction? { KtProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleRequest request=%s", request ) // Check if we should skip handling this transition val shouldHandleRequest = when { Loading @@ -418,43 +436,63 @@ class DesktopTasksController( } val task: RunningTaskInfo = request.triggerTask val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) // Check if we should switch a fullscreen task to freeform if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) { // If there are any visible desktop tasks, switch the task to freeform if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) { return when { // If display has tasks stashed, handle as stashed launch desktopModeTaskRepository.isStashed(task.displayId) -> handleStashedTaskLaunch(task) // Check if fullscreen task should be updated task.windowingMode == WINDOWING_MODE_FULLSCREEN -> handleFullscreenTaskLaunch(task) // Check if freeform task should be updated task.windowingMode == WINDOWING_MODE_FREEFORM -> handleFreeformTaskLaunch(task) else -> null } } private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? { val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) { KtProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: switch fullscreen task to freeform on transition" + "DesktopTasksController: switch freeform task to fullscreen oon transition" + " taskId=%d", task.taskId ) return WindowContainerTransaction().also { wct -> addMoveToDesktopChanges(wct, task.token) addMoveToFullscreenChanges(wct, task.token) } } return null } // CHeck if we should switch a freeform task to fullscreen if (task.windowingMode == WINDOWING_MODE_FREEFORM) { // If no visible desktop tasks, switch this task to freeform as the transition came // outside of this controller if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) { private fun handleFullscreenTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? { val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) { KtProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: switch freeform task to fullscreen oon transition" + "DesktopTasksController: switch fullscreen task to freeform on transition" + " taskId=%d", task.taskId ) return WindowContainerTransaction().also { wct -> addMoveToFullscreenChanges(wct, task.token) } addMoveToDesktopChanges(wct, task.token) } } return null } private fun handleStashedTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction { KtProtoLog.d( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: launch apps with stashed on transition taskId=%d", task.taskId ) val wct = WindowContainerTransaction() bringDesktopAppsToFront(task.displayId, wct) addMoveToDesktopChanges(wct, task.token) desktopModeTaskRepository.setStashed(task.displayId, false) return wct } private fun addMoveToDesktopChanges( wct: WindowContainerTransaction, token: WindowContainerToken Loading Loading @@ -658,8 +696,46 @@ class DesktopTasksController( @BinderThread private class IDesktopModeImpl(private var controller: DesktopTasksController?) : IDesktopMode.Stub(), ExternalInterfaceBinder { private lateinit var remoteListener: SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener> private val listener: VisibleTasksListener = object : VisibleTasksListener { override fun onVisibilityChanged(displayId: Int, visible: Boolean) { // TODO(b/261234402): move visibility from sysui state to listener remoteListener.call { l -> l.onVisibilityChanged(displayId, visible) } } override fun onStashedChanged(displayId: Int, stashed: Boolean) { KtProtoLog.v( WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: onStashedChanged stashed=%b display=%d", stashed, displayId ) remoteListener.call { l -> l.onStashedChanged(displayId, stashed) } } } init { remoteListener = SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>( controller, { c -> c.desktopModeTaskRepository.addVisibleTasksListener( listener, c.mainExecutor ) }, { c -> c.desktopModeTaskRepository.removeVisibleTasksListener(listener) } ) } /** Invalidates this instance, preventing future calls from updating the controller. */ override fun invalidate() { remoteListener.unregister() controller = null } Loading @@ -670,6 +746,13 @@ class DesktopTasksController( ) { c -> c.showDesktopApps(displayId) } } override fun stashDesktopApps(displayId: Int) { ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "stashDesktopApps" ) { c -> c.stashDesktopApps(displayId) } } override fun getVisibleTaskCount(displayId: Int): Int { val result = IntArray(1) ExecutorUtils.executeRemoteCallWithTaskPermission( Loading @@ -680,6 +763,18 @@ class DesktopTasksController( ) return result[0] } override fun setTaskListener(listener: IDesktopTaskListener?) { KtProtoLog.v( WM_SHELL_DESKTOP_MODE, "IDesktopModeImpl: set task listener=%s", listener ?: "null" ) ExecutorUtils.executeRemoteCallWithTaskPermission( controller, "setTaskListener" ) { _ -> listener?.let { remoteListener.register(it) } ?: remoteListener.unregister() } } } companion object { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +8 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.wm.shell.desktopmode; import com.android.wm.shell.desktopmode.IDesktopTaskListener; /** * Interface that is exposed to remote callers to manipulate desktop mode features. */ Loading @@ -24,6 +26,12 @@ interface IDesktopMode { /** Show apps on the desktop on the given display */ void showDesktopApps(int displayId); /** Stash apps on the desktop to allow launching another app from home screen */ void stashDesktopApps(int displayId); /** Get count of visible desktop tasks on the given display */ int getVisibleTaskCount(int displayId); /** Set listener that will receive callbacks about updates to desktop tasks */ oneway void setTaskListener(IDesktopTaskListener listener); } No newline at end of file
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl 0 → 100644 +30 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.desktopmode; /** * Allows external processes to register a listener in WMShell to get updates about desktop task * state. */ interface IDesktopTaskListener { /** Desktop task visibility has change. Visible if at least 1 task is visible. */ oneway void onVisibilityChanged(int displayId, boolean visible); /** Desktop task stashed status has changed. */ oneway void onStashedChanged(int displayId, boolean stashed); } No newline at end of file