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.intentresolver.contentpreview 18 19 import android.content.ContentInterface 20 import android.content.Intent 21 import android.database.MatrixCursor 22 import android.media.MediaMetadata 23 import android.net.Uri 24 import android.platform.test.annotations.EnableFlags 25 import android.platform.test.flag.junit.FlagsParameterization 26 import android.platform.test.flag.junit.SetFlagsRule 27 import android.provider.DocumentsContract 28 import android.provider.Downloads 29 import android.provider.OpenableColumns 30 import com.android.intentresolver.Flags.FLAG_INDIVIDUAL_METADATA_TITLE_READ 31 import com.google.common.truth.Truth.assertThat 32 import kotlin.coroutines.EmptyCoroutineContext 33 import kotlinx.coroutines.CoroutineScope 34 import kotlinx.coroutines.ExperimentalCoroutinesApi 35 import kotlinx.coroutines.flow.toList 36 import kotlinx.coroutines.test.TestScope 37 import kotlinx.coroutines.test.UnconfinedTestDispatcher 38 import kotlinx.coroutines.test.runTest 39 import org.junit.Rule 40 import org.junit.Test 41 import org.junit.runner.RunWith 42 import org.junit.runners.Parameterized 43 import org.mockito.kotlin.any 44 import org.mockito.kotlin.anyOrNull 45 import org.mockito.kotlin.eq 46 import org.mockito.kotlin.mock 47 import org.mockito.kotlin.never 48 import org.mockito.kotlin.times 49 import org.mockito.kotlin.verify 50 import org.mockito.kotlin.whenever 51 52 @RunWith(Parameterized::class) 53 @OptIn(ExperimentalCoroutinesApi::class) 54 class PreviewDataProviderTest(flags: FlagsParameterization) { 55 private val contentResolver = mock<ContentInterface>() 56 private val mimeTypeClassifier = DefaultMimeTypeClassifier 57 private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher()) 58 @get:Rule val setFlagsRule = SetFlagsRule(flags) 59 createDataProvidernull60 private fun createDataProvider( 61 targetIntent: Intent, 62 scope: CoroutineScope = testScope, 63 additionalContentUri: Uri? = null, 64 resolver: ContentInterface = contentResolver, 65 typeClassifier: MimeTypeClassifier = mimeTypeClassifier, 66 ) = PreviewDataProvider(scope, targetIntent, additionalContentUri, resolver, typeClassifier) 67 68 @Test 69 fun test_nonSendIntentAction_resolvesToTextPreviewUiSynchronously() { 70 val targetIntent = Intent(Intent.ACTION_VIEW) 71 val testSubject = createDataProvider(targetIntent) 72 73 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT) 74 verify(contentResolver, never()).getType(any()) 75 } 76 77 @Test test_sendSingleTextFileWithoutPreview_resolvesToFilePreviewUinull78 fun test_sendSingleTextFileWithoutPreview_resolvesToFilePreviewUi() = 79 testScope.runTest { 80 val fileName = "notes.txt" 81 val uri = Uri.parse("content://org.pkg.app/$fileName") 82 val targetIntent = 83 Intent(Intent.ACTION_SEND).apply { 84 putExtra(Intent.EXTRA_STREAM, uri) 85 type = "text/plain" 86 } 87 whenever(contentResolver.getType(uri)).thenReturn("text/plain") 88 val testSubject = createDataProvider(targetIntent) 89 90 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE) 91 assertThat(testSubject.uriCount).isEqualTo(1) 92 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 93 assertThat(testSubject.getFirstFileName()).isEqualTo(fileName) 94 verify(contentResolver, times(1)).getType(any()) 95 } 96 97 @Test test_sendSingleTextFileWithDisplayNameAndTitle_displayNameTakesPrecedenceOverTitlenull98 fun test_sendSingleTextFileWithDisplayNameAndTitle_displayNameTakesPrecedenceOverTitle() = 99 testScope.runTest { 100 val uri = Uri.parse("content://org.pkg.app/1234") 101 val targetIntent = 102 Intent(Intent.ACTION_SEND).apply { 103 putExtra(Intent.EXTRA_STREAM, uri) 104 type = "text/plain" 105 } 106 whenever(contentResolver.getType(uri)).thenReturn("text/plain") 107 val title = "Notes" 108 val displayName = "Notes.txt" 109 whenever(contentResolver.query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull())) 110 .thenReturn( 111 MatrixCursor(arrayOf(Downloads.Impl.COLUMN_TITLE, OpenableColumns.DISPLAY_NAME)) 112 .apply { addRow(arrayOf(title, displayName)) } 113 ) 114 contentResolver.setTitle(uri, title) 115 contentResolver.setDisplayName(uri, displayName) 116 val testSubject = createDataProvider(targetIntent) 117 118 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE) 119 assertThat(testSubject.getFirstFileName()).isEqualTo(displayName) 120 } 121 122 @Test test_sendIntentWithoutUris_resolvesToTextPreviewUiSynchronouslynull123 fun test_sendIntentWithoutUris_resolvesToTextPreviewUiSynchronously() { 124 val targetIntent = Intent(Intent.ACTION_SEND).apply { type = "image/png" } 125 val testSubject = createDataProvider(targetIntent) 126 127 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT) 128 verify(contentResolver, never()).getType(any()) 129 } 130 131 @Test test_sendSingleImage_resolvesToImagePreviewUinull132 fun test_sendSingleImage_resolvesToImagePreviewUi() { 133 val uri = Uri.parse("content://org.pkg.app/image.png") 134 val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) } 135 whenever(contentResolver.getType(uri)).thenReturn("image/png") 136 val testSubject = createDataProvider(targetIntent) 137 138 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE) 139 assertThat(testSubject.uriCount).isEqualTo(1) 140 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 141 assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri) 142 verify(contentResolver, times(1)).getType(any()) 143 } 144 145 @Test test_sendSingleFile_resolvesToFilePreviewUinull146 fun test_sendSingleFile_resolvesToFilePreviewUi() = 147 testScope.runTest { 148 val fileName = "paper.pdf" 149 val uri = Uri.parse("content://org.pkg.app/$fileName") 150 val targetIntent = 151 Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) } 152 whenever(contentResolver.getType(uri)).thenReturn("application/pdf") 153 val testSubject = createDataProvider(targetIntent) 154 155 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE) 156 assertThat(testSubject.uriCount).isEqualTo(1) 157 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 158 assertThat(testSubject.firstFileInfo?.previewUri).isNull() 159 assertThat(testSubject.getFirstFileName()).isEqualTo(fileName) 160 verify(contentResolver, times(1)).getType(any()) 161 } 162 163 @Test test_sendSingleImageWithFailingGetType_resolvesToFilePreviewUinull164 fun test_sendSingleImageWithFailingGetType_resolvesToFilePreviewUi() = 165 testScope.runTest { 166 val fileName = "image.png" 167 val uri = Uri.parse("content://org.pkg.app/$fileName") 168 val targetIntent = 169 Intent(Intent.ACTION_SEND).apply { 170 type = "image/png" 171 putExtra(Intent.EXTRA_STREAM, uri) 172 } 173 whenever(contentResolver.getType(uri)).thenThrow(SecurityException("test failure")) 174 val testSubject = createDataProvider(targetIntent) 175 176 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE) 177 assertThat(testSubject.uriCount).isEqualTo(1) 178 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 179 assertThat(testSubject.firstFileInfo?.previewUri).isNull() 180 assertThat(testSubject.getFirstFileName()).isEqualTo(fileName) 181 verify(contentResolver, times(1)).getType(any()) 182 } 183 184 @Test test_sendSingleFileWithFailingMetadata_resolvesToFilePreviewUinull185 fun test_sendSingleFileWithFailingMetadata_resolvesToFilePreviewUi() = 186 testScope.runTest { 187 val fileName = "manual.pdf" 188 val uri = Uri.parse("content://org.pkg.app/$fileName") 189 val targetIntent = 190 Intent(Intent.ACTION_SEND).apply { 191 type = "application/pdf" 192 putExtra(Intent.EXTRA_STREAM, uri) 193 } 194 whenever(contentResolver.getType(uri)).thenReturn("application/pdf") 195 whenever(contentResolver.getStreamTypes(uri, "*/*")) 196 .thenThrow(SecurityException("test failure")) 197 whenever(contentResolver.query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull())) 198 .thenThrow(SecurityException("test failure")) 199 val testSubject = createDataProvider(targetIntent) 200 201 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE) 202 assertThat(testSubject.uriCount).isEqualTo(1) 203 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 204 assertThat(testSubject.firstFileInfo?.previewUri).isNull() 205 assertThat(testSubject.getFirstFileName()).isEqualTo(fileName) 206 verify(contentResolver, times(1)).getType(any()) 207 } 208 209 @Test 210 @EnableFlags(FLAG_INDIVIDUAL_METADATA_TITLE_READ) test_sendSingleImageWithFailingGetTypeDisjointTitleRead_resolvesToFilePreviewUinull211 fun test_sendSingleImageWithFailingGetTypeDisjointTitleRead_resolvesToFilePreviewUi() = 212 testScope.runTest { 213 val uri = Uri.parse("content://org.pkg.app/image.png") 214 val targetIntent = 215 Intent(Intent.ACTION_SEND).apply { 216 type = "image/png" 217 putExtra(Intent.EXTRA_STREAM, uri) 218 } 219 whenever(contentResolver.getType(uri)).thenThrow(SecurityException("test failure")) 220 val title = "Image Title" 221 contentResolver.setTitle(uri, title) 222 val testSubject = createDataProvider(targetIntent) 223 224 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE) 225 assertThat(testSubject.uriCount).isEqualTo(1) 226 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 227 assertThat(testSubject.firstFileInfo?.previewUri).isNull() 228 assertThat(testSubject.getFirstFileName()).isEqualTo(title) 229 verify(contentResolver, times(1)).getType(any()) 230 } 231 232 @Test test_sendSingleFileWithFailingImageMetadata_resolvesToFilePreviewUinull233 fun test_sendSingleFileWithFailingImageMetadata_resolvesToFilePreviewUi() = 234 testScope.runTest { 235 val fileName = "notes.pdf" 236 val uri = Uri.parse("content://org.pkg.app/$fileName") 237 val targetIntent = 238 Intent(Intent.ACTION_SEND).apply { 239 type = "application/pdf" 240 putExtra(Intent.EXTRA_STREAM, uri) 241 } 242 whenever(contentResolver.getType(uri)).thenReturn("application/pdf") 243 whenever(contentResolver.getStreamTypes(uri, "*/*")) 244 .thenThrow(SecurityException("test failure")) 245 whenever(contentResolver.query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull())) 246 .thenThrow(SecurityException("test failure")) 247 val testSubject = createDataProvider(targetIntent) 248 249 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE) 250 assertThat(testSubject.uriCount).isEqualTo(1) 251 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 252 assertThat(testSubject.firstFileInfo?.previewUri).isNull() 253 assertThat(testSubject.getFirstFileName()).isEqualTo(fileName) 254 verify(contentResolver, times(1)).getType(any()) 255 } 256 257 @Test 258 @EnableFlags(FLAG_INDIVIDUAL_METADATA_TITLE_READ) test_sendSingleFileWithFailingImageMetadataIndividualTitleRead_resolvesToFilePreviewUinull259 fun test_sendSingleFileWithFailingImageMetadataIndividualTitleRead_resolvesToFilePreviewUi() = 260 testScope.runTest { 261 val uri = Uri.parse("content://org.pkg.app/image.png") 262 val targetIntent = 263 Intent(Intent.ACTION_SEND).apply { 264 type = "image/png" 265 putExtra(Intent.EXTRA_STREAM, uri) 266 } 267 whenever(contentResolver.getStreamTypes(uri, "*/*")) 268 .thenThrow(SecurityException("test failure")) 269 whenever(contentResolver.query(uri, ICON_METADATA_COLUMNS, null, null)) 270 .thenThrow(SecurityException("test failure")) 271 val displayName = "display name" 272 contentResolver.setDisplayName(uri, displayName) 273 val testSubject = createDataProvider(targetIntent) 274 275 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE) 276 assertThat(testSubject.uriCount).isEqualTo(1) 277 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 278 assertThat(testSubject.firstFileInfo?.previewUri).isNull() 279 assertThat(testSubject.getFirstFileName()).isEqualTo(displayName) 280 verify(contentResolver, times(1)).getType(any()) 281 } 282 283 @Test test_SingleFileUriWithImageTypeInGetStreamTypes_useImagePreviewUinull284 fun test_SingleFileUriWithImageTypeInGetStreamTypes_useImagePreviewUi() { 285 val uri = Uri.parse("content://org.pkg.app/paper.pdf") 286 val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) } 287 whenever(contentResolver.getStreamTypes(uri, "*/*")) 288 .thenReturn(arrayOf("application/pdf", "image/png")) 289 val testSubject = createDataProvider(targetIntent) 290 291 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE) 292 assertThat(testSubject.uriCount).isEqualTo(1) 293 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 294 assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri) 295 verify(contentResolver, times(1)).getType(any()) 296 } 297 298 @Test test_SingleNonImageUriWithThumbnailFlag_useImagePreviewUinull299 fun test_SingleNonImageUriWithThumbnailFlag_useImagePreviewUi() { 300 testMetadataToImagePreview( 301 columns = arrayOf(DocumentsContract.Document.COLUMN_FLAGS), 302 values = 303 arrayOf( 304 DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL or 305 DocumentsContract.Document.FLAG_SUPPORTS_METADATA 306 ), 307 ) 308 } 309 310 @Test test_SingleNonImageUriWithMetadataIconUri_useImagePreviewUinull311 fun test_SingleNonImageUriWithMetadataIconUri_useImagePreviewUi() { 312 testMetadataToImagePreview( 313 columns = arrayOf(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI), 314 values = arrayOf("content://org.pkg.app/test.pdf?thumbnail"), 315 ) 316 } 317 testMetadataToImagePreviewnull318 private fun testMetadataToImagePreview(columns: Array<String>, values: Array<Any>) { 319 val uri = Uri.parse("content://org.pkg.app/test.pdf") 320 val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) } 321 whenever(contentResolver.getType(uri)).thenReturn("application/pdf") 322 val cursor = MatrixCursor(columns).apply { addRow(values) } 323 whenever(contentResolver.query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull())) 324 .thenReturn(cursor) 325 326 val testSubject = createDataProvider(targetIntent) 327 328 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE) 329 assertThat(testSubject.uriCount).isEqualTo(1) 330 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 331 assertThat(testSubject.firstFileInfo?.previewUri).isNotNull() 332 verify(contentResolver, times(1)).getType(any()) 333 assertThat(cursor.isClosed).isTrue() 334 } 335 336 @Test test_emptyQueryResult_cursorGetsClosednull337 fun test_emptyQueryResult_cursorGetsClosed() { 338 val uri = Uri.parse("content://org.pkg.app/test.pdf") 339 val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) } 340 whenever(contentResolver.getType(uri)).thenReturn("application/pdf") 341 val cursor = MatrixCursor(emptyArray()) 342 whenever(contentResolver.query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull())) 343 .thenReturn(cursor) 344 345 val testSubject = createDataProvider(targetIntent) 346 347 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE) 348 verify(contentResolver, times(1)).query(eq(uri), anyOrNull(), anyOrNull(), anyOrNull()) 349 assertThat(cursor.isClosed).isTrue() 350 } 351 352 @Test test_multipleImageUri_useImagePreviewUinull353 fun test_multipleImageUri_useImagePreviewUi() { 354 val uri1 = Uri.parse("content://org.pkg.app/test.png") 355 val uri2 = Uri.parse("content://org.pkg.app/test.jpg") 356 val targetIntent = 357 Intent(Intent.ACTION_SEND_MULTIPLE).apply { 358 putExtra( 359 Intent.EXTRA_STREAM, 360 ArrayList<Uri>().apply { 361 add(uri1) 362 add(uri2) 363 }, 364 ) 365 } 366 whenever(contentResolver.getType(uri1)).thenReturn("image/png") 367 whenever(contentResolver.getType(uri2)).thenReturn("image/jpeg") 368 val testSubject = createDataProvider(targetIntent) 369 370 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE) 371 assertThat(testSubject.uriCount).isEqualTo(2) 372 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri1) 373 assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri1) 374 // preview type can be determined by the first URI type 375 verify(contentResolver, times(1)).getType(any()) 376 } 377 378 @Test test_SomeImageUri_useImagePreviewUinull379 fun test_SomeImageUri_useImagePreviewUi() { 380 val uri1 = Uri.parse("content://org.pkg.app/test.png") 381 val uri2 = Uri.parse("content://org.pkg.app/test.pdf") 382 whenever(contentResolver.getType(uri1)).thenReturn("image/png") 383 whenever(contentResolver.getType(uri2)).thenReturn("application/pdf") 384 val targetIntent = 385 Intent(Intent.ACTION_SEND_MULTIPLE).apply { 386 putExtra( 387 Intent.EXTRA_STREAM, 388 ArrayList<Uri>().apply { 389 add(uri1) 390 add(uri2) 391 }, 392 ) 393 } 394 val testSubject = createDataProvider(targetIntent) 395 396 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE) 397 assertThat(testSubject.uriCount).isEqualTo(2) 398 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri1) 399 assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri1) 400 // preview type can be determined by the first URI type 401 verify(contentResolver, times(1)).getType(any()) 402 } 403 404 @Test test_someFileUrisWithPreview_useImagePreviewUinull405 fun test_someFileUrisWithPreview_useImagePreviewUi() { 406 val uri1 = Uri.parse("content://org.pkg.app/test.mp4") 407 val uri2 = Uri.parse("content://org.pkg.app/test.pdf") 408 val targetIntent = 409 Intent(Intent.ACTION_SEND_MULTIPLE).apply { 410 putExtra( 411 Intent.EXTRA_STREAM, 412 ArrayList<Uri>().apply { 413 add(uri1) 414 add(uri2) 415 }, 416 ) 417 } 418 whenever(contentResolver.getType(uri1)).thenReturn("video/mpeg4") 419 whenever(contentResolver.getStreamTypes(uri1, "*/*")).thenReturn(arrayOf("image/png")) 420 whenever(contentResolver.getType(uri2)).thenReturn("application/pdf") 421 val testSubject = createDataProvider(targetIntent) 422 423 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE) 424 assertThat(testSubject.uriCount).isEqualTo(2) 425 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri1) 426 assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri1) 427 verify(contentResolver, times(2)).getType(any()) 428 } 429 430 @Test test_allFileUrisWithoutPreview_useFilePreviewUinull431 fun test_allFileUrisWithoutPreview_useFilePreviewUi() = 432 testScope.runTest { 433 val firstFileName = "test.html" 434 val uri1 = Uri.parse("content://org.pkg.app/$firstFileName") 435 val uri2 = Uri.parse("content://org.pkg.app/test.pdf") 436 val targetIntent = 437 Intent(Intent.ACTION_SEND_MULTIPLE).apply { 438 putExtra( 439 Intent.EXTRA_STREAM, 440 ArrayList<Uri>().apply { 441 add(uri1) 442 add(uri2) 443 }, 444 ) 445 } 446 whenever(contentResolver.getType(uri1)).thenReturn("text/html") 447 whenever(contentResolver.getType(uri2)).thenReturn("application/pdf") 448 val testSubject = createDataProvider(targetIntent) 449 450 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE) 451 assertThat(testSubject.uriCount).isEqualTo(2) 452 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri1) 453 assertThat(testSubject.firstFileInfo?.previewUri).isNull() 454 assertThat(testSubject.getFirstFileName()).isEqualTo(firstFileName) 455 verify(contentResolver, times(2)).getType(any()) 456 } 457 458 @Test test_imagePreviewFileInfoFlow_dataLoadedOncenull459 fun test_imagePreviewFileInfoFlow_dataLoadedOnce() = 460 testScope.runTest { 461 val uri1 = Uri.parse("content://org.pkg.app/test.html") 462 val uri2 = Uri.parse("content://org.pkg.app/test.pdf") 463 val targetIntent = 464 Intent(Intent.ACTION_SEND_MULTIPLE).apply { 465 putExtra( 466 Intent.EXTRA_STREAM, 467 ArrayList<Uri>().apply { 468 add(uri1) 469 add(uri2) 470 }, 471 ) 472 } 473 whenever(contentResolver.getType(uri1)).thenReturn("text/html") 474 whenever(contentResolver.getType(uri2)).thenReturn("application/pdf") 475 whenever(contentResolver.getStreamTypes(uri1, "*/*")) 476 .thenReturn(arrayOf("text/html", "image/jpeg")) 477 whenever(contentResolver.getStreamTypes(uri2, "*/*")) 478 .thenReturn(arrayOf("application/pdf", "image/png")) 479 val testSubject = createDataProvider(targetIntent) 480 481 val fileInfoListOne = testSubject.imagePreviewFileInfoFlow.toList() 482 val fileInfoListTwo = testSubject.imagePreviewFileInfoFlow.toList() 483 484 assertThat(fileInfoListOne).hasSize(2) 485 assertThat(fileInfoListOne).containsAtLeastElementsIn(fileInfoListTwo).inOrder() 486 487 verify(contentResolver, times(1)).getType(uri1) 488 verify(contentResolver, times(1)).getStreamTypes(uri1, "*/*") 489 verify(contentResolver, times(1)).getType(uri2) 490 verify(contentResolver, times(1)).getStreamTypes(uri2, "*/*") 491 } 492 493 @Test sendImageWithAdditionalContentUri_showPayloadTogglingUinull494 fun sendImageWithAdditionalContentUri_showPayloadTogglingUi() { 495 val uri = Uri.parse("content://org.pkg.app/image.png") 496 val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) } 497 whenever(contentResolver.getType(uri)).thenReturn("image/png") 498 val testSubject = 499 createDataProvider( 500 targetIntent, 501 additionalContentUri = Uri.parse("content://org.pkg.app.extracontent"), 502 ) 503 504 assertThat(testSubject.previewType) 505 .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION) 506 assertThat(testSubject.uriCount).isEqualTo(1) 507 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 508 assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri) 509 verify(contentResolver, times(1)).getType(any()) 510 } 511 512 @Test sendItemsWithAdditionalContentUriWithSameAuthority_showImagePreviewUinull513 fun sendItemsWithAdditionalContentUriWithSameAuthority_showImagePreviewUi() { 514 val uri = Uri.parse("content://org.pkg.app/image.png") 515 val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) } 516 whenever(contentResolver.getType(uri)).thenReturn("image/png") 517 val testSubject = 518 createDataProvider( 519 targetIntent, 520 additionalContentUri = Uri.parse("content://org.pkg.app/extracontent"), 521 ) 522 523 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE) 524 assertThat(testSubject.uriCount).isEqualTo(1) 525 assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri) 526 assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri) 527 verify(contentResolver, times(1)).getType(any()) 528 } 529 530 @Test test_nonSendIntentActionWithAdditionalContentUri_resolvesToTextPreviewUiSynchronouslynull531 fun test_nonSendIntentActionWithAdditionalContentUri_resolvesToTextPreviewUiSynchronously() { 532 val targetIntent = Intent(Intent.ACTION_VIEW) 533 val testSubject = 534 createDataProvider( 535 targetIntent, 536 additionalContentUri = Uri.parse("content://org.pkg.app/extracontent"), 537 ) 538 539 assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT) 540 verify(contentResolver, never()).getType(any()) 541 } 542 543 companion object { 544 @JvmStatic 545 @Parameterized.Parameters(name = "{0}") parametersnull546 fun parameters(): List<FlagsParameterization> = 547 FlagsParameterization.allCombinationsOf(FLAG_INDIVIDUAL_METADATA_TITLE_READ) 548 } 549 } 550 551 private fun ContentInterface.setDisplayName(uri: Uri, displayName: String) = 552 setMetadata(uri, arrayOf(OpenableColumns.DISPLAY_NAME), arrayOf(displayName)) 553 554 private fun ContentInterface.setTitle(uri: Uri, title: String) = 555 setMetadata(uri, arrayOf(Downloads.Impl.COLUMN_TITLE), arrayOf(title)) 556 557 private fun ContentInterface.setMetadata(uri: Uri, columns: Array<String>, values: Array<String>) { 558 whenever(query(uri, columns, null, null)) 559 .thenReturn(MatrixCursor(columns).apply { addRow(values) }) 560 } 561