• 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.launcher3.model
18 
19 import android.app.PendingIntent
20 import android.content.ComponentName
21 import android.content.Intent
22 import android.content.pm.PackageInstaller.SessionInfo
23 import android.os.UserHandle
24 import androidx.test.platform.app.InstrumentationRegistry
25 import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
26 import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
27 import com.android.launcher3.model.FirstScreenBroadcastHelper.MAX_BROADCAST_SIZE
28 import com.android.launcher3.model.FirstScreenBroadcastHelper.getTotalItemCount
29 import com.android.launcher3.model.FirstScreenBroadcastHelper.truncateModelForBroadcast
30 import com.android.launcher3.model.data.FolderInfo
31 import com.android.launcher3.model.data.LauncherAppWidgetInfo
32 import com.android.launcher3.model.data.WorkspaceItemInfo
33 import com.android.launcher3.util.PackageManagerHelper
34 import com.android.launcher3.util.PackageUserKey
35 import junit.framework.Assert.assertEquals
36 import org.junit.Test
37 import org.mockito.ArgumentCaptor
38 import org.mockito.kotlin.mock
39 import org.mockito.kotlin.spy
40 import org.mockito.kotlin.verify
41 import org.mockito.kotlin.whenever
42 
43 class FirstScreenBroadcastHelperTest {
44     private val context = spy(InstrumentationRegistry.getInstrumentation().targetContext)
45     private val mockPmHelper = mock<PackageManagerHelper>()
46     private val expectedAppPackage = "appPackageExpected"
47     private val expectedComponentName = ComponentName(expectedAppPackage, "expectedClass")
48     private val expectedInstallerPackage = "installerPackage"
49     private val expectedIntent =
<lambda>null50         Intent().apply {
51             component = expectedComponentName
52             setPackage(expectedAppPackage)
53         }
54     private val unexpectedAppPackage = "appPackageUnexpected"
55     private val unexpectedComponentName = ComponentName(expectedAppPackage, "unexpectedClass")
56     private val firstScreenItems =
57         listOf(
<lambda>null58             WorkspaceItemInfo().apply {
59                 container = CONTAINER_DESKTOP
60                 intent = expectedIntent
61             },
<lambda>null62             WorkspaceItemInfo().apply {
63                 container = CONTAINER_HOTSEAT
64                 intent = expectedIntent
65             },
<lambda>null66             LauncherAppWidgetInfo().apply { providerName = expectedComponentName }
67         )
68 
69     @Test
Broadcast Models are created with Pending Items from first screennull70     fun `Broadcast Models are created with Pending Items from first screen`() {
71         // Given
72         val sessionInfoExpected =
73             SessionInfo().apply {
74                 installerPackageName = expectedInstallerPackage
75                 appPackageName = expectedAppPackage
76             }
77         val sessionInfoUnexpected =
78             SessionInfo().apply {
79                 installerPackageName = expectedInstallerPackage
80                 appPackageName = unexpectedAppPackage
81             }
82         val sessionInfoMap: HashMap<PackageUserKey, SessionInfo> =
83             hashMapOf(
84                 PackageUserKey(unexpectedAppPackage, UserHandle(0)) to sessionInfoExpected,
85                 PackageUserKey(expectedAppPackage, UserHandle(0)) to sessionInfoUnexpected
86             )
87 
88         // When
89         val actualResult =
90             FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
91                 packageManagerHelper = mockPmHelper,
92                 firstScreenItems = firstScreenItems,
93                 userKeyToSessionMap = sessionInfoMap,
94                 allWidgets = listOf()
95             )
96 
97         // Then
98         val expectedResult =
99             listOf(
100                 FirstScreenBroadcastModel(
101                     installerPackage = expectedInstallerPackage,
102                     pendingWorkspaceItems = mutableSetOf(expectedAppPackage),
103                     pendingHotseatItems = mutableSetOf(expectedAppPackage),
104                     pendingWidgetItems = mutableSetOf(expectedAppPackage)
105                 )
106             )
107 
108         assertEquals(expectedResult, actualResult)
109     }
110 
111     @Test
Broadcast Models are created with Installed Items from first screennull112     fun `Broadcast Models are created with Installed Items from first screen`() {
113         // Given
114         whenever(mockPmHelper.getAppInstallerPackage(expectedAppPackage))
115             .thenReturn(expectedInstallerPackage)
116 
117         // When
118         val actualResult =
119             FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
120                 packageManagerHelper = mockPmHelper,
121                 firstScreenItems = firstScreenItems,
122                 userKeyToSessionMap = hashMapOf(),
123                 allWidgets =
124                     listOf(
125                         LauncherAppWidgetInfo().apply {
126                             providerName = expectedComponentName
127                             screenId = 0
128                         }
129                     )
130             )
131 
132         // Then
133         val expectedResult =
134             listOf(
135                 FirstScreenBroadcastModel(
136                     installerPackage = expectedInstallerPackage,
137                     installedHotseatItems = mutableSetOf(expectedAppPackage),
138                     installedWorkspaceItems = mutableSetOf(expectedAppPackage),
139                     firstScreenInstalledWidgets = mutableSetOf(expectedAppPackage)
140                 )
141             )
142         assertEquals(expectedResult, actualResult)
143     }
144 
145     @Test
Broadcast Models are created with Installed Widgets from every screennull146     fun `Broadcast Models are created with Installed Widgets from every screen`() {
147         // Given
148         val expectedAppPackage2 = "appPackageExpected2"
149         val expectedComponentName2 = ComponentName(expectedAppPackage2, "expectedClass2")
150         whenever(mockPmHelper.getAppInstallerPackage(expectedAppPackage))
151             .thenReturn(expectedInstallerPackage)
152         whenever(mockPmHelper.getAppInstallerPackage(expectedAppPackage2))
153             .thenReturn(expectedInstallerPackage)
154 
155         // When
156         val actualResult =
157             FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
158                 packageManagerHelper = mockPmHelper,
159                 firstScreenItems = listOf(),
160                 userKeyToSessionMap = hashMapOf(),
161                 allWidgets =
162                     listOf(
163                         LauncherAppWidgetInfo().apply {
164                             providerName = expectedComponentName
165                             screenId = 0
166                         },
167                         LauncherAppWidgetInfo().apply {
168                             providerName = expectedComponentName2
169                             screenId = 1
170                         },
171                         LauncherAppWidgetInfo().apply {
172                             providerName = unexpectedComponentName
173                             screenId = 0
174                         }
175                     )
176             )
177 
178         // Then
179         val expectedResult =
180             listOf(
181                 FirstScreenBroadcastModel(
182                     installerPackage = expectedInstallerPackage,
183                     installedHotseatItems = mutableSetOf(),
184                     installedWorkspaceItems = mutableSetOf(),
185                     firstScreenInstalledWidgets = mutableSetOf(expectedAppPackage),
186                     secondaryScreenInstalledWidgets = mutableSetOf(expectedAppPackage2)
187                 )
188             )
189         assertEquals(expectedResult, actualResult)
190     }
191 
192     @Test
Broadcast Models are created with Pending Items in Collections from the first screennull193     fun `Broadcast Models are created with Pending Items in Collections from the first screen`() {
194         // Given
195         val sessionInfoExpected =
196             SessionInfo().apply {
197                 installerPackageName = expectedInstallerPackage
198                 appPackageName = expectedAppPackage
199             }
200         val sessionInfoUnexpected =
201             SessionInfo().apply {
202                 installerPackageName = expectedInstallerPackage
203                 appPackageName = unexpectedAppPackage
204             }
205         val sessionInfoMap: HashMap<PackageUserKey, SessionInfo> =
206             hashMapOf(
207                 PackageUserKey(unexpectedAppPackage, UserHandle(0)) to sessionInfoExpected,
208                 PackageUserKey(expectedAppPackage, UserHandle(0)) to sessionInfoUnexpected,
209             )
210         val expectedItemInfo = WorkspaceItemInfo().apply { intent = expectedIntent }
211         val expectedFolderInfo = FolderInfo().apply { add(expectedItemInfo) }
212         val firstScreenItems = listOf(expectedFolderInfo)
213 
214         // When
215         val actualResult =
216             FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
217                 packageManagerHelper = mockPmHelper,
218                 firstScreenItems = firstScreenItems,
219                 userKeyToSessionMap = sessionInfoMap,
220                 allWidgets = listOf()
221             )
222 
223         // Then
224         val expectedResult =
225             listOf(
226                 FirstScreenBroadcastModel(
227                     installerPackage = expectedInstallerPackage,
228                     pendingCollectionItems = mutableSetOf(expectedAppPackage)
229                 )
230             )
231         assertEquals(expectedResult, actualResult)
232     }
233 
234     @Test
Models with too many items get truncated to max Broadcast sizenull235     fun `Models with too many items get truncated to max Broadcast size`() {
236         // given
237         val broadcastModel =
238             FirstScreenBroadcastModel(
239                 installerPackage = expectedInstallerPackage,
240                 pendingCollectionItems =
241                     mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
242                 pendingWorkspaceItems =
243                     mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
244                 pendingHotseatItems =
245                     mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
246                 pendingWidgetItems =
247                     mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
248                 installedWorkspaceItems =
249                     mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
250                 installedHotseatItems =
251                     mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
252                 firstScreenInstalledWidgets =
253                     mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
254                 secondaryScreenInstalledWidgets =
255                     mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } }
256             )
257 
258         // When
259         broadcastModel.truncateModelForBroadcast()
260 
261         // Then
262         assertEquals(MAX_BROADCAST_SIZE, broadcastModel.getTotalItemCount())
263     }
264 
265     @Test
Broadcast truncates installed Hotseat items before other installed itemsnull266     fun `Broadcast truncates installed Hotseat items before other installed items`() {
267         // Given
268         val broadcastModel =
269             FirstScreenBroadcastModel(
270                 installerPackage = expectedInstallerPackage,
271                 installedWorkspaceItems =
272                     mutableSetOf<String>().apply { repeat(50) { add(it.toString()) } },
273                 firstScreenInstalledWidgets =
274                     mutableSetOf<String>().apply { repeat(10) { add(it.toString()) } },
275                 secondaryScreenInstalledWidgets =
276                     mutableSetOf<String>().apply { repeat(10) { add((it + 10).toString()) } },
277                 installedHotseatItems =
278                     mutableSetOf<String>().apply { repeat(10) { add(it.toString()) } },
279             )
280 
281         // When
282         broadcastModel.truncateModelForBroadcast()
283 
284         // Then
285         assertEquals(MAX_BROADCAST_SIZE, broadcastModel.getTotalItemCount())
286         assertEquals(50, broadcastModel.installedWorkspaceItems.size)
287         assertEquals(10, broadcastModel.firstScreenInstalledWidgets.size)
288         assertEquals(10, broadcastModel.secondaryScreenInstalledWidgets.size)
289         assertEquals(0, broadcastModel.installedHotseatItems.size)
290     }
291 
292     @Test
Broadcast truncates Widgets before the rest of the first screen itemsnull293     fun `Broadcast truncates Widgets before the rest of the first screen items`() {
294         // Given
295         val broadcastModel =
296             FirstScreenBroadcastModel(
297                 installerPackage = expectedInstallerPackage,
298                 installedWorkspaceItems =
299                     mutableSetOf<String>().apply { repeat(70) { add(it.toString()) } },
300                 firstScreenInstalledWidgets =
301                     mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
302                 secondaryScreenInstalledWidgets =
303                     mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
304             )
305 
306         // When
307         broadcastModel.truncateModelForBroadcast()
308 
309         // Then
310         assertEquals(MAX_BROADCAST_SIZE, broadcastModel.getTotalItemCount())
311         assertEquals(70, broadcastModel.installedWorkspaceItems.size)
312         assertEquals(0, broadcastModel.firstScreenInstalledWidgets.size)
313         assertEquals(0, broadcastModel.secondaryScreenInstalledWidgets.size)
314     }
315 
316     @Test
Broadcasts are correctly formed with Extras for each Installernull317     fun `Broadcasts are correctly formed with Extras for each Installer`() {
318         // Given
319         val broadcastModels: List<FirstScreenBroadcastModel> =
320             listOf(
321                 FirstScreenBroadcastModel(
322                     installerPackage = expectedInstallerPackage,
323                     pendingCollectionItems = mutableSetOf("pendingCollectionItem"),
324                     pendingWorkspaceItems = mutableSetOf("pendingWorkspaceItem"),
325                     pendingHotseatItems = mutableSetOf("pendingHotseatItems"),
326                     pendingWidgetItems = mutableSetOf("pendingWidgetItems"),
327                     installedWorkspaceItems = mutableSetOf("installedWorkspaceItems"),
328                     installedHotseatItems = mutableSetOf("installedHotseatItems"),
329                     firstScreenInstalledWidgets = mutableSetOf("firstScreenInstalledWidgetItems"),
330                     secondaryScreenInstalledWidgets = mutableSetOf("secondaryInstalledWidgetItems")
331                 )
332             )
333         val expectedPendingIntent =
334             PendingIntent.getActivity(
335                 context,
336                 0 /* requestCode */,
337                 Intent(),
338                 PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
339             )
340 
341         // When
342         FirstScreenBroadcastHelper.sendBroadcastsForModels(context, broadcastModels)
343 
344         // Then
345         val argumentCaptor = ArgumentCaptor.forClass(Intent::class.java)
346         verify(context).sendBroadcast(argumentCaptor.capture())
347 
348         assertEquals(
349             "com.android.launcher3.action.FIRST_SCREEN_ACTIVE_INSTALLS",
350             argumentCaptor.value.action
351         )
352         assertEquals(expectedInstallerPackage, argumentCaptor.value.`package`)
353         assertEquals(
354             expectedPendingIntent,
355             argumentCaptor.value.getParcelableExtra("verificationToken")
356         )
357         assertEquals(
358             arrayListOf("pendingCollectionItem"),
359             argumentCaptor.value.getStringArrayListExtra("folderItem")
360         )
361         assertEquals(
362             arrayListOf("pendingWorkspaceItem"),
363             argumentCaptor.value.getStringArrayListExtra("workspaceItem")
364         )
365         assertEquals(
366             arrayListOf("pendingHotseatItems"),
367             argumentCaptor.value.getStringArrayListExtra("hotseatItem")
368         )
369         assertEquals(
370             arrayListOf("pendingWidgetItems"),
371             argumentCaptor.value.getStringArrayListExtra("widgetItem")
372         )
373         assertEquals(
374             arrayListOf("installedWorkspaceItems"),
375             argumentCaptor.value.getStringArrayListExtra("workspaceInstalledItems")
376         )
377         assertEquals(
378             arrayListOf("installedHotseatItems"),
379             argumentCaptor.value.getStringArrayListExtra("hotseatInstalledItems")
380         )
381         assertEquals(
382             arrayListOf("firstScreenInstalledWidgetItems", "secondaryInstalledWidgetItems"),
383             argumentCaptor.value.getStringArrayListExtra("widgetInstalledItems")
384         )
385     }
386 }
387