• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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