Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34157d2c authored by Ats Jenk's avatar Ats Jenk Committed by Android (Google) Code Review
Browse files

Merge "Add an API to desktop mode to stash desktop apps" into udc-qpr-dev

parents 94862db1 22d0bbb9
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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
        }
    }
}
+34 −0
Original line number Diff line number Diff line
@@ -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).
@@ -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.
     */
@@ -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) {}
    }
}
+119 −24
Original line number Diff line number Diff line
@@ -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
@@ -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)
@@ -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 {
@@ -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
@@ -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
        }

@@ -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(
@@ -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 {
+8 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.wm.shell.desktopmode;

import com.android.wm.shell.desktopmode.IDesktopTaskListener;

/**
 * Interface that is exposed to remote callers to manipulate desktop mode features.
 */
@@ -24,6 +26,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
+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