1 /*
2  * Copyright (C) 2023 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.Notification
20 import android.app.PendingIntent
21 import android.content.ComponentName
22 import android.content.Intent
23 import android.graphics.Bitmap
24 import android.graphics.drawable.Icon
25 import android.net.Uri
26 import android.os.UserHandle
27 import androidx.test.filters.SmallTest
28 import com.android.systemui.SysuiTestCase
29 import com.android.systemui.flags.FakeFeatureFlags
30 import com.android.systemui.screenshot.ScreenshotController.SaveImageInBackgroundData
31 import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType
32 import com.android.systemui.util.mockito.any
33 import com.android.systemui.util.mockito.eq
34 import com.android.systemui.util.mockito.mock
35 import com.android.systemui.util.mockito.whenever
36 import java.util.concurrent.CompletableFuture
37 import java.util.function.Supplier
38 import org.junit.Assert.assertEquals
39 import org.junit.Assert.assertNull
40 import org.junit.Before
41 import org.junit.Test
42 
43 @SmallTest
44 class SaveImageInBackgroundTaskTest : SysuiTestCase() {
45     private val imageExporter = mock<ImageExporter>()
46     private val smartActions = mock<ScreenshotSmartActions>()
47     private val smartActionsProvider = mock<ScreenshotNotificationSmartActionsProvider>()
48     private val saveImageData = SaveImageInBackgroundData()
49     private val sharedTransitionSupplier =
50         mock<Supplier<ScreenshotController.SavedImageData.ActionTransition>>()
51     private val testScreenshotId: String = "testScreenshotId"
52     private val testBitmap = mock<Bitmap>()
53     private val testUser = UserHandle.getUserHandleForUid(0)
54     private val testIcon = mock<Icon>()
55     private val testImageTime = 1234.toLong()
56     private val flags = FakeFeatureFlags()
57 
58     private val smartActionsUriFuture = mock<CompletableFuture<List<Notification.Action>>>()
59     private val smartActionsFuture = mock<CompletableFuture<List<Notification.Action>>>()
60 
61     private val testUri: Uri = Uri.parse("testUri")
62     private val intent =
63         Intent(Intent.ACTION_SEND)
64             .setComponent(
65                 ComponentName.unflattenFromString(
66                     "com.google.android.test/com.google.android.test.TestActivity"
67                 )
68             )
69     private val immutablePendingIntent =
70         PendingIntent.getBroadcast(
71             mContext,
72             0,
73             intent,
74             PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
75         )
76     private val mutablePendingIntent =
77         PendingIntent.getBroadcast(
78             mContext,
79             0,
80             intent,
81             PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE
82         )
83 
84     private val saveImageTask =
85         SaveImageInBackgroundTask(
86             mContext,
87             flags,
88             imageExporter,
89             smartActions,
90             saveImageData,
91             sharedTransitionSupplier,
92             smartActionsProvider,
93         )
94 
95     @Before
96     fun setup() {
97         whenever(
98                 smartActions.getSmartActionsFuture(
99                     eq(testScreenshotId),
100                     any(Uri::class.java),
101                     eq(testBitmap),
102                     eq(smartActionsProvider),
103                     any(ScreenshotSmartActionType::class.java),
104                     any(Boolean::class.java),
105                     eq(testUser)
106                 )
107             )
108             .thenReturn(smartActionsUriFuture)
109         whenever(
110                 smartActions.getSmartActionsFuture(
111                     eq(testScreenshotId),
112                     eq(null),
113                     eq(testBitmap),
114                     eq(smartActionsProvider),
115                     any(ScreenshotSmartActionType::class.java),
116                     any(Boolean::class.java),
117                     eq(testUser)
118                 )
119             )
120             .thenReturn(smartActionsFuture)
121     }
122 
123     @Test
124     fun testQueryQuickShare_noAction() {
125         whenever(
126                 smartActions.getSmartActions(
127                     eq(testScreenshotId),
128                     eq(smartActionsFuture),
129                     any(Int::class.java),
130                     eq(smartActionsProvider),
131                     eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
132                 )
133             )
134             .thenReturn(ArrayList<Notification.Action>())
135 
136         val quickShareAction =
137             saveImageTask.queryQuickShareAction(testScreenshotId, testBitmap, testUser, testUri)
138 
139         assertNull(quickShareAction)
140     }
141 
142     @Test
143     fun testQueryQuickShare_withActions() {
144         val actions = ArrayList<Notification.Action>()
145         actions.add(constructAction("Action One", mutablePendingIntent))
146         actions.add(constructAction("Action Two", mutablePendingIntent))
147         whenever(
148                 smartActions.getSmartActions(
149                     eq(testScreenshotId),
150                     eq(smartActionsUriFuture),
151                     any(Int::class.java),
152                     eq(smartActionsProvider),
153                     eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
154                 )
155             )
156             .thenReturn(actions)
157 
158         val quickShareAction =
159             saveImageTask.queryQuickShareAction(testScreenshotId, testBitmap, testUser, testUri)!!
160 
161         assertEquals("Action One", quickShareAction.title)
162         assertEquals(mutablePendingIntent, quickShareAction.actionIntent)
163     }
164 
165     @Test
166     fun testCreateQuickShareAction_originalWasNull_returnsNull() {
167         val quickShareAction =
168             saveImageTask.createQuickShareAction(
169                 null,
170                 testScreenshotId,
171                 testUri,
172                 testImageTime,
173                 testBitmap,
174                 testUser
175             )
176 
177         assertNull(quickShareAction)
178     }
179 
180     @Test
181     fun testCreateQuickShareAction_immutableIntentDifferentAction_returnsNull() {
182         val actions = ArrayList<Notification.Action>()
183         actions.add(constructAction("New Test Action", immutablePendingIntent))
184         whenever(
185                 smartActions.getSmartActions(
186                     eq(testScreenshotId),
187                     eq(smartActionsUriFuture),
188                     any(Int::class.java),
189                     eq(smartActionsProvider),
190                     eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
191                 )
192             )
193             .thenReturn(actions)
194         val origAction = constructAction("Old Test Action", immutablePendingIntent)
195 
196         val quickShareAction =
197             saveImageTask.createQuickShareAction(
198                 origAction,
199                 testScreenshotId,
200                 testUri,
201                 testImageTime,
202                 testBitmap,
203                 testUser,
204             )
205 
206         assertNull(quickShareAction)
207     }
208 
209     @Test
210     fun testCreateQuickShareAction_mutableIntent_returnsSafeIntent() {
211         val actions = ArrayList<Notification.Action>()
212         val action = constructAction("Action One", mutablePendingIntent)
213         actions.add(action)
214         whenever(
215                 smartActions.getSmartActions(
216                     eq(testScreenshotId),
217                     eq(smartActionsUriFuture),
218                     any(Int::class.java),
219                     eq(smartActionsProvider),
220                     eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
221                 )
222             )
223             .thenReturn(actions)
224 
225         val quickShareAction =
226             saveImageTask.createQuickShareAction(
227                 constructAction("Test Action", mutablePendingIntent),
228                 testScreenshotId,
229                 testUri,
230                 testImageTime,
231                 testBitmap,
232                 testUser
233             )
234         val quickSharePendingIntent =
235             quickShareAction.actionIntent.intent.extras!!.getParcelable(
236                 ScreenshotController.EXTRA_ACTION_INTENT,
237                 PendingIntent::class.java
238             )
239 
240         assertEquals("Test Action", quickShareAction.title)
241         assertEquals(mutablePendingIntent, quickSharePendingIntent)
242     }
243 
244     @Test
245     fun testCreateQuickShareAction_immutableIntent_returnsSafeIntent() {
246         val actions = ArrayList<Notification.Action>()
247         val action = constructAction("Test Action", immutablePendingIntent)
248         actions.add(action)
249         whenever(
250                 smartActions.getSmartActions(
251                     eq(testScreenshotId),
252                     eq(smartActionsUriFuture),
253                     any(Int::class.java),
254                     eq(smartActionsProvider),
255                     eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION)
256                 )
257             )
258             .thenReturn(actions)
259 
260         val quickShareAction =
261             saveImageTask.createQuickShareAction(
262                 constructAction("Test Action", immutablePendingIntent),
263                 testScreenshotId,
264                 testUri,
265                 testImageTime,
266                 testBitmap,
267                 testUser,
268             )!!
269 
270         assertEquals("Test Action", quickShareAction.title)
271         assertEquals(
272             immutablePendingIntent,
273             quickShareAction.actionIntent.intent.extras!!.getParcelable(
274                 ScreenshotController.EXTRA_ACTION_INTENT,
275                 PendingIntent::class.java
276             )
277         )
278     }
279 
280     private fun constructAction(title: String, intent: PendingIntent): Notification.Action {
281         return Notification.Action.Builder(testIcon, title, intent).build()
282     }
283 }
284