1 /*
2  * Copyright (C) 2022 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.systemui.screenshot
18 
19 import android.app.ActivityTaskManager.RootTaskInfo
20 import android.app.IActivityTaskManager
21 import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
22 import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
23 import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
24 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
25 import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
26 import android.content.ComponentName
27 import android.content.Context
28 import android.graphics.Rect
29 import android.os.UserHandle
30 import android.os.UserManager
31 import android.testing.AndroidTestingRunner
32 import com.android.systemui.SysuiTestCase
33 import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
34 import com.android.systemui.settings.FakeDisplayTracker
35 import com.android.systemui.util.mockito.mock
36 import com.google.common.truth.Truth.assertThat
37 import kotlinx.coroutines.Dispatchers
38 import kotlinx.coroutines.runBlocking
39 import org.junit.Test
40 import org.junit.runner.RunWith
41 
42 // The following values are chosen to be distinct from commonly seen real values
43 private const val DISPLAY_ID = 100
44 private const val PRIMARY_USER = 2000
45 private const val MANAGED_PROFILE_USER = 3000
46 
47 @RunWith(AndroidTestingRunner::class)
48 class ScreenshotPolicyImplTest : SysuiTestCase() {
49 
50     @Test
51     fun testToDisplayContentInfo() {
52         assertThat(fullScreenWorkProfileTask.toDisplayContentInfo())
53             .isEqualTo(
54                 DisplayContentInfo(
55                     ComponentName(
56                         "com.google.android.apps.nbu.files",
57                         "com.google.android.apps.nbu.files.home.HomeActivity"
58                     ),
59                     Rect(0, 0, 1080, 2400),
60                     UserHandle.of(MANAGED_PROFILE_USER),
61                     65))
62     }
63 
64     @Test
65     fun findPrimaryContent_ignoresPipTask() = runBlocking {
66         val policy = fakeTasksPolicyImpl(
67             mContext,
68             shadeExpanded = false,
69             tasks = listOf(
70                     pipTask,
71                     fullScreenWorkProfileTask,
72                     launcherTask,
73                     emptyTask)
74         )
75 
76         val info = policy.findPrimaryContent(DISPLAY_ID)
77         assertThat(info).isEqualTo(fullScreenWorkProfileTask.toDisplayContentInfo())
78     }
79 
80     @Test
81     fun findPrimaryContent_shadeExpanded_ignoresTopTask() = runBlocking {
82         val policy = fakeTasksPolicyImpl(
83             mContext,
84             shadeExpanded = true,
85             tasks = listOf(
86                 fullScreenWorkProfileTask,
87                 launcherTask,
88                 emptyTask)
89         )
90 
91         val info = policy.findPrimaryContent(DISPLAY_ID)
92         assertThat(info).isEqualTo(policy.systemUiContent)
93     }
94 
95     @Test
96     fun findPrimaryContent_emptyTaskList() = runBlocking {
97         val policy = fakeTasksPolicyImpl(
98             mContext,
99             shadeExpanded = false,
100             tasks = listOf()
101         )
102 
103         val info = policy.findPrimaryContent(DISPLAY_ID)
104         assertThat(info).isEqualTo(policy.systemUiContent)
105     }
106 
107     @Test
108     fun findPrimaryContent_workProfileNotOnTop() = runBlocking {
109         val policy = fakeTasksPolicyImpl(
110             mContext,
111             shadeExpanded = false,
112             tasks = listOf(
113                 launcherTask,
114                 fullScreenWorkProfileTask,
115                 emptyTask)
116         )
117 
118         val info = policy.findPrimaryContent(DISPLAY_ID)
119         assertThat(info).isEqualTo(launcherTask.toDisplayContentInfo())
120     }
121 
122     private fun fakeTasksPolicyImpl(
123         context: Context,
124         shadeExpanded: Boolean,
125         tasks: List<RootTaskInfo>
126     ): ScreenshotPolicyImpl {
127         val userManager = mock<UserManager>()
128         val atmService = mock<IActivityTaskManager>()
129         val dispatcher = Dispatchers.Unconfined
130         val displayTracker = FakeDisplayTracker(context)
131 
132         return object : ScreenshotPolicyImpl(context, userManager, atmService, dispatcher,
133                 displayTracker) {
134             override suspend fun isManagedProfile(userId: Int) = (userId == MANAGED_PROFILE_USER)
135             override suspend fun getAllRootTaskInfosOnDisplay(displayId: Int) = tasks
136             override suspend fun isNotificationShadeExpanded() = shadeExpanded
137         }
138     }
139 
140     private val pipTask = RootTaskInfo().apply {
141         configuration.windowConfiguration.apply {
142             windowingMode = WINDOWING_MODE_PINNED
143             bounds = Rect(628, 1885, 1038, 2295)
144             activityType = ACTIVITY_TYPE_STANDARD
145         }
146         displayId = DISPLAY_ID
147         userId = PRIMARY_USER
148         taskId = 66
149         visible = true
150         isVisible = true
151         isRunning = true
152         numActivities = 1
153         topActivity = ComponentName(
154             "com.google.android.youtube",
155             "com.google.android.apps.youtube.app.watchwhile.WatchWhileActivity"
156         )
157         childTaskIds = intArrayOf(66)
158         childTaskNames = arrayOf("com.google.android.youtube/" +
159                 "com.google.android.youtube.app.honeycomb.Shell\$HomeActivity")
160         childTaskUserIds = intArrayOf(0)
161         childTaskBounds = arrayOf(Rect(628, 1885, 1038, 2295))
162     }
163 
164     private val fullScreenWorkProfileTask = RootTaskInfo().apply {
165         configuration.windowConfiguration.apply {
166             windowingMode = WINDOWING_MODE_FULLSCREEN
167             bounds = Rect(0, 0, 1080, 2400)
168             activityType = ACTIVITY_TYPE_STANDARD
169         }
170         displayId = DISPLAY_ID
171         userId = MANAGED_PROFILE_USER
172         taskId = 65
173         visible = true
174         isVisible = true
175         isRunning = true
176         numActivities = 1
177         topActivity = ComponentName(
178             "com.google.android.apps.nbu.files",
179             "com.google.android.apps.nbu.files.home.HomeActivity"
180         )
181         childTaskIds = intArrayOf(65)
182         childTaskNames = arrayOf("com.google.android.apps.nbu.files/" +
183                 "com.google.android.apps.nbu.files.home.HomeActivity")
184         childTaskUserIds = intArrayOf(MANAGED_PROFILE_USER)
185         childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400))
186     }
187 
188     private val launcherTask = RootTaskInfo().apply {
189         configuration.windowConfiguration.apply {
190             windowingMode = WINDOWING_MODE_FULLSCREEN
191             bounds = Rect(0, 0, 1080, 2400)
192             activityType = ACTIVITY_TYPE_HOME
193         }
194         displayId = DISPLAY_ID
195         taskId = 1
196         userId = PRIMARY_USER
197         visible = true
198         isVisible = true
199         isRunning = true
200         numActivities = 1
201         topActivity = ComponentName(
202             "com.google.android.apps.nexuslauncher",
203             "com.google.android.apps.nexuslauncher.NexusLauncherActivity",
204         )
205         childTaskIds = intArrayOf(1)
206         childTaskNames = arrayOf("com.google.android.apps.nexuslauncher/" +
207                 "com.google.android.apps.nexuslauncher.NexusLauncherActivity")
208         childTaskUserIds = intArrayOf(0)
209         childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400))
210     }
211 
212     private val emptyTask = RootTaskInfo().apply {
213         configuration.windowConfiguration.apply {
214             windowingMode = WINDOWING_MODE_FULLSCREEN
215             bounds = Rect(0, 0, 1080, 2400)
216             activityType = ACTIVITY_TYPE_UNDEFINED
217         }
218         displayId = DISPLAY_ID
219         taskId = 2
220         userId = PRIMARY_USER
221         visible = false
222         isVisible = false
223         isRunning = false
224         numActivities = 0
225         childTaskIds = intArrayOf(3, 4)
226         childTaskNames = arrayOf("", "")
227         childTaskUserIds = intArrayOf(0, 0)
228         childTaskBounds = arrayOf(Rect(0, 0, 1080, 2400), Rect(0, 2400, 1080, 4800))
229     }
230 }
231