• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.desktopmode
18 
19 import android.app.ActivityManager.RunningTaskInfo
20 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
21 import android.window.DesktopModeFlags
22 import com.android.internal.protolog.ProtoLog
23 import com.android.wm.shell.freeform.TaskChangeListener
24 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
25 
26 /** Manages tasks handling specific to Android Desktop Mode. */
27 class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUserRepositories) :
28     TaskChangeListener {
29 
onTaskOpeningnull30     override fun onTaskOpening(taskInfo: RunningTaskInfo) {
31         logD("onTaskOpening for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
32         val desktopRepository: DesktopRepository =
33             desktopUserRepositories.getProfile(taskInfo.userId)
34         if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
35             desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
36             return
37         }
38         if (isFreeformTask(taskInfo) && !desktopRepository.isActiveTask(taskInfo.taskId)) {
39             desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
40         }
41     }
42 
onTaskChangingnull43     override fun onTaskChanging(taskInfo: RunningTaskInfo) {
44         logD("onTaskChanging for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
45         val desktopRepository: DesktopRepository =
46             desktopUserRepositories.getProfile(taskInfo.userId)
47         // TODO: b/394281403 - with multiple desks, it's possible to have a non-freeform task
48         //  inside a desk, so this should be decoupled from windowing mode.
49         //  Also, changes in/out of desks are handled by the [DesksTransitionObserver], which has
50         //  more specific information about the desk involved in the transition, which might be
51         //  more accurate than assuming it's always the default/active desk in the display, as this
52         //  method does.
53         // Case 1: When the task change is from a task in the desktop repository which is now
54         // fullscreen,
55         // remove the task from the desktop repository since it is no longer a freeform task.
56         if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
57             desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
58         } else if (isFreeformTask(taskInfo)) {
59             // If the task is already active in the repository, then moves task to the front,
60             // else adds the task.
61             desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
62         }
63     }
64 
65     // This method should only be used for scenarios where the task info changes are not propagated
66     // to
67     // [DesktopTaskChangeListener#onTaskChanging] via [TransitionsObserver].
68     // Any changes to [DesktopRepository] from this method should be made carefully to minimize risk
69     // of race conditions and possible duplications with [onTaskChanging].
onNonTransitionTaskChangingnull70     override fun onNonTransitionTaskChanging(taskInfo: RunningTaskInfo) {
71         // TODO: b/367268953 - Propagate usages from FreeformTaskListener to this method.
72         logD(
73             "onNonTransitionTaskChanging for taskId=%d, displayId=%d",
74             taskInfo.taskId,
75             taskInfo.displayId,
76         )
77     }
78 
onTaskMovingToFrontnull79     override fun onTaskMovingToFront(taskInfo: RunningTaskInfo) {
80         logD("onTaskMovingToFront for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
81         val desktopRepository: DesktopRepository =
82             desktopUserRepositories.getProfile(taskInfo.userId)
83         // When the task change is from a task in the desktop repository which is now fullscreen,
84         // remove the task from the desktop repository since it is no longer a freeform task.
85         if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
86             desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
87         }
88         if (isFreeformTask(taskInfo)) {
89             // If the task is already active in the repository, then it only moves the task to the
90             // front.
91             desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
92         }
93     }
94 
onTaskMovingToBacknull95     override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
96         val desktopRepository: DesktopRepository =
97             desktopUserRepositories.getProfile(taskInfo.userId)
98         if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
99         logD("onTaskMovingToBack for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
100         desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, /* isVisible= */ false)
101     }
102 
onTaskClosingnull103     override fun onTaskClosing(taskInfo: RunningTaskInfo) {
104         logD("onTaskClosing for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
105         val desktopRepository: DesktopRepository =
106             desktopUserRepositories.getProfile(taskInfo.userId)
107         if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
108         // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
109         if (
110             !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue ||
111                 desktopRepository.isClosingTask(taskInfo.taskId)
112         ) {
113             // A task that's vanishing should be removed:
114             // - If it's closed by the X button which means it's marked as a closing task.
115             desktopRepository.removeClosingTask(taskInfo.taskId)
116             desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
117         } else {
118             desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, isVisible = false)
119             desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
120         }
121     }
122 
isFreeformTasknull123     private fun isFreeformTask(taskInfo: RunningTaskInfo): Boolean =
124         taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
125 
126     private fun logD(msg: String, vararg arguments: Any?) {
127         ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
128     }
129 
130     companion object {
131         private const val TAG = "DesktopTaskChangeListener"
132     }
133 }
134