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