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.launcher3.taskbar
18 
19 import android.app.ActivityManager.RunningTaskInfo
20 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
21 import android.content.ComponentName
22 import android.content.Intent
23 import android.os.Process
24 import android.os.UserHandle
25 import android.testing.AndroidTestingRunner
26 import com.android.launcher3.model.data.AppInfo
27 import com.android.launcher3.model.data.ItemInfo
28 import com.android.launcher3.statehandlers.DesktopVisibilityController
29 import com.android.quickstep.RecentsModel
30 import com.google.common.truth.Truth.assertThat
31 import org.junit.Before
32 import org.junit.Rule
33 import org.junit.Test
34 import org.junit.runner.RunWith
35 import org.mockito.Mock
36 import org.mockito.junit.MockitoJUnit
37 import org.mockito.kotlin.whenever
38 
39 @RunWith(AndroidTestingRunner::class)
40 class TaskbarRecentAppsControllerTest : TaskbarBaseTestCase() {
41 
42     @get:Rule val mockitoRule = MockitoJUnit.rule()
43 
44     @Mock private lateinit var mockRecentsModel: RecentsModel
45     @Mock private lateinit var mockDesktopVisibilityController: DesktopVisibilityController
46 
47     private var nextTaskId: Int = 500
48 
49     private lateinit var recentAppsController: TaskbarRecentAppsController
50     private lateinit var userHandle: UserHandle
51 
52     @Before
setUpnull53     fun setUp() {
54         super.setup()
55         userHandle = Process.myUserHandle()
56         recentAppsController =
57             TaskbarRecentAppsController(mockRecentsModel) { mockDesktopVisibilityController }
58         recentAppsController.init(taskbarControllers)
59         recentAppsController.isEnabled = true
60         recentAppsController.setApps(
61             ALL_APP_PACKAGES.map { createTestAppInfo(packageName = it) }.toTypedArray()
62         )
63     }
64 
65     @Test
updateHotseatItemInfos_notInDesktopMode_returnsExistingHotseatItemsnull66     fun updateHotseatItemInfos_notInDesktopMode_returnsExistingHotseatItems() {
67         setInDesktopMode(false)
68         val hotseatItems =
69             createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
70 
71         assertThat(recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray()))
72             .isEqualTo(hotseatItems.toTypedArray())
73     }
74 
75     @Test
updateHotseatItemInfos_notInDesktopMode_runningApps_returnsExistingHotseatItemsnull76     fun updateHotseatItemInfos_notInDesktopMode_runningApps_returnsExistingHotseatItems() {
77         setInDesktopMode(false)
78         val hotseatPackages = listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2)
79         val hotseatItems = createHotseatItemsFromPackageNames(hotseatPackages)
80         val runningTasks =
81             createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
82         whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
83         recentAppsController.updateRunningApps()
84 
85         val newHotseatItems =
86             recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
87 
88         assertThat(newHotseatItems.map { it?.targetPackage })
89             .containsExactlyElementsIn(hotseatPackages)
90     }
91 
92     @Test
updateHotseatItemInfos_noRunningApps_returnsExistingHotseatItemsnull93     fun updateHotseatItemInfos_noRunningApps_returnsExistingHotseatItems() {
94         setInDesktopMode(true)
95         val hotseatItems =
96             createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
97 
98         assertThat(recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray()))
99             .isEqualTo(hotseatItems.toTypedArray())
100     }
101 
102     @Test
updateHotseatItemInfos_returnsExistingHotseatItemsAndRunningAppsnull103     fun updateHotseatItemInfos_returnsExistingHotseatItemsAndRunningApps() {
104         setInDesktopMode(true)
105         val hotseatItems =
106             createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
107         val runningTasks =
108             createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
109         whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
110         recentAppsController.updateRunningApps()
111 
112         val newHotseatItems =
113             recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
114 
115         val expectedPackages =
116             listOf(
117                 HOTSEAT_PACKAGE_1,
118                 HOTSEAT_PACKAGE_2,
119                 RUNNING_APP_PACKAGE_1,
120                 RUNNING_APP_PACKAGE_2,
121             )
122         assertThat(newHotseatItems.map { it?.targetPackage })
123             .containsExactlyElementsIn(expectedPackages)
124     }
125 
126     @Test
updateHotseatItemInfos_runningAppIsHotseatItem_returnsDistinctItemsnull127     fun updateHotseatItemInfos_runningAppIsHotseatItem_returnsDistinctItems() {
128         setInDesktopMode(true)
129         val hotseatItems =
130             createHotseatItemsFromPackageNames(listOf(HOTSEAT_PACKAGE_1, HOTSEAT_PACKAGE_2))
131         val runningTasks =
132             createDesktopTasksFromPackageNames(
133                 listOf(HOTSEAT_PACKAGE_1, RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
134             )
135         whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
136         recentAppsController.updateRunningApps()
137 
138         val newHotseatItems =
139             recentAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
140 
141         val expectedPackages =
142             listOf(
143                 HOTSEAT_PACKAGE_1,
144                 HOTSEAT_PACKAGE_2,
145                 RUNNING_APP_PACKAGE_1,
146                 RUNNING_APP_PACKAGE_2,
147             )
148         assertThat(newHotseatItems.map { it?.targetPackage })
149             .containsExactlyElementsIn(expectedPackages)
150     }
151 
152     @Test
getRunningApps_notInDesktopMode_returnsEmptySetnull153     fun getRunningApps_notInDesktopMode_returnsEmptySet() {
154         setInDesktopMode(false)
155         val runningTasks =
156             createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
157         whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
158         recentAppsController.updateRunningApps()
159 
160         assertThat(recentAppsController.runningApps).isEmpty()
161         assertThat(recentAppsController.minimizedApps).isEmpty()
162     }
163 
164     @Test
getRunningApps_inDesktopMode_returnsRunningAppsnull165     fun getRunningApps_inDesktopMode_returnsRunningApps() {
166         setInDesktopMode(true)
167         val runningTasks =
168             createDesktopTasksFromPackageNames(listOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
169         whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
170         recentAppsController.updateRunningApps()
171 
172         assertThat(recentAppsController.runningApps)
173             .containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
174         assertThat(recentAppsController.minimizedApps).isEmpty()
175     }
176 
177     @Test
getMinimizedApps_inDesktopMode_returnsAllAppsRunningAndInvisibleAppsMinimizednull178     fun getMinimizedApps_inDesktopMode_returnsAllAppsRunningAndInvisibleAppsMinimized() {
179         setInDesktopMode(true)
180         val runningTasks =
181             ArrayList(
182                 listOf(
183                     createDesktopTaskInfo(RUNNING_APP_PACKAGE_1) { isVisible = true },
184                     createDesktopTaskInfo(RUNNING_APP_PACKAGE_2) { isVisible = true },
185                     createDesktopTaskInfo(RUNNING_APP_PACKAGE_3) { isVisible = false },
186                 )
187             )
188         whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
189         recentAppsController.updateRunningApps()
190 
191         assertThat(recentAppsController.runningApps)
192             .containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_3)
193         assertThat(recentAppsController.minimizedApps).containsExactly(RUNNING_APP_PACKAGE_3)
194     }
195 
createHotseatItemsFromPackageNamesnull196     private fun createHotseatItemsFromPackageNames(packageNames: List<String>): List<ItemInfo> {
197         return packageNames.map { createTestAppInfo(packageName = it) }
198     }
199 
createDesktopTasksFromPackageNamesnull200     private fun createDesktopTasksFromPackageNames(
201         packageNames: List<String>
202     ): ArrayList<RunningTaskInfo> {
203         return ArrayList(packageNames.map { createDesktopTaskInfo(packageName = it) })
204     }
205 
createDesktopTaskInfonull206     private fun createDesktopTaskInfo(
207         packageName: String,
208         init: RunningTaskInfo.() -> Unit = { isVisible = true },
209     ): RunningTaskInfo {
<lambda>null210         return RunningTaskInfo().apply {
211             taskId = nextTaskId++
212             configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
213             realActivity = ComponentName(packageName, "TestActivity")
214             init()
215         }
216     }
217 
createTestAppInfonull218     private fun createTestAppInfo(
219         packageName: String = "testPackageName",
220         className: String = "testClassName"
221     ) = AppInfo(ComponentName(packageName, className), className /* title */, userHandle, Intent())
222 
223     private fun setInDesktopMode(inDesktopMode: Boolean) {
224         whenever(mockDesktopVisibilityController.areDesktopTasksVisible()).thenReturn(inDesktopMode)
225     }
226 
227     private companion object {
228         const val HOTSEAT_PACKAGE_1 = "hotseat1"
229         const val HOTSEAT_PACKAGE_2 = "hotseat2"
230         const val RUNNING_APP_PACKAGE_1 = "running1"
231         const val RUNNING_APP_PACKAGE_2 = "running2"
232         const val RUNNING_APP_PACKAGE_3 = "running3"
233         val ALL_APP_PACKAGES =
234             listOf(
235                 HOTSEAT_PACKAGE_1,
236                 HOTSEAT_PACKAGE_2,
237                 RUNNING_APP_PACKAGE_1,
238                 RUNNING_APP_PACKAGE_2,
239                 RUNNING_APP_PACKAGE_3,
240             )
241     }
242 }
243