• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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.photopicker.tests.core.events
18 
19 import android.content.ContentResolver
20 import android.content.Context
21 import android.content.pm.PackageManager
22 import android.content.pm.UserProperties
23 import android.media.ApplicationMediaCapabilities
24 import android.media.MediaFeature.HdrType
25 import android.net.Uri
26 import android.os.Build
27 import android.os.Parcel
28 import android.os.UserHandle
29 import android.os.UserManager
30 import androidx.test.ext.junit.runners.AndroidJUnit4
31 import androidx.test.filters.SdkSuppress
32 import androidx.test.filters.SmallTest
33 import androidx.test.platform.app.InstrumentationRegistry
34 import com.android.modules.utils.build.SdkLevel
35 import com.android.photopicker.R
36 import com.android.photopicker.core.configuration.PhotopickerRuntimeEnv
37 import com.android.photopicker.core.configuration.TestPhotopickerConfiguration
38 import com.android.photopicker.core.configuration.provideTestConfigurationFlow
39 import com.android.photopicker.core.events.Event
40 import com.android.photopicker.core.events.Events
41 import com.android.photopicker.core.events.Telemetry
42 import com.android.photopicker.core.events.dispatchPhotopickerExpansionStateChangedEvent
43 import com.android.photopicker.core.events.dispatchReportPhotopickerApiInfoEvent
44 import com.android.photopicker.core.events.dispatchReportPhotopickerMediaItemStatusEvent
45 import com.android.photopicker.core.events.dispatchReportPhotopickerSessionInfoEvent
46 import com.android.photopicker.core.events.dispatchReportPickerAppMediaCapabilities
47 import com.android.photopicker.core.events.generatePickerSessionId
48 import com.android.photopicker.core.features.FeatureManager
49 import com.android.photopicker.core.features.FeatureToken
50 import com.android.photopicker.core.selection.Selection
51 import com.android.photopicker.core.selection.SelectionImpl
52 import com.android.photopicker.core.user.UserMonitor
53 import com.android.photopicker.data.DataService
54 import com.android.photopicker.data.TestDataServiceImpl
55 import com.android.photopicker.data.TestPrefetchDataService
56 import com.android.photopicker.data.model.Group
57 import com.android.photopicker.data.model.Media
58 import com.android.photopicker.data.model.MediaSource
59 import com.android.photopicker.features.search.SearchFeature
60 import com.android.photopicker.util.test.mockSystemService
61 import com.android.photopicker.util.test.whenever
62 import com.google.common.truth.Truth.assertThat
63 import dagger.Lazy
64 import kotlinx.coroutines.ExperimentalCoroutinesApi
65 import kotlinx.coroutines.flow.toList
66 import kotlinx.coroutines.launch
67 import kotlinx.coroutines.test.StandardTestDispatcher
68 import kotlinx.coroutines.test.TestScope
69 import kotlinx.coroutines.test.advanceTimeBy
70 import kotlinx.coroutines.test.runTest
71 import org.junit.Test
72 import org.junit.runner.RunWith
73 import org.mockito.ArgumentMatchers.any
74 import org.mockito.Mockito.anyInt
75 import org.mockito.Mockito.mock
76 
77 /** Unit tests for the telemetry event dispatchers */
78 @SmallTest
79 @RunWith(AndroidJUnit4::class)
80 @OptIn(ExperimentalCoroutinesApi::class)
81 class DispatchersTest {
82     private val sessionId = generatePickerSessionId()
83     private val packageUid = 12345
84 
85     private val photopickerConfiguration =
<lambda>null86         TestPhotopickerConfiguration.build {
87             action(value = "")
88             sessionId(value = sessionId)
89             callingPackageUid(value = packageUid)
90             runtimeEnv(value = PhotopickerRuntimeEnv.EMBEDDED)
91             mimeTypes(arrayListOf("image/jpeg"))
92         }
93     private val mediaItemAlbum =
94         Group.Album(
95             id = "",
96             pickerId = 0L,
97             authority = "",
98             dateTakenMillisLong = 0L,
99             displayName = "",
100             coverUri = Uri.EMPTY,
101             coverMediaSource = MediaSource.LOCAL,
102         )
103     private val mediaItem =
104         Media.Image(
105             mediaId = "",
106             pickerId = 0L,
107             index = 9999,
108             authority = "",
109             mediaSource = MediaSource.LOCAL,
110             mediaUri = Uri.EMPTY,
111             glideLoadableUri = Uri.EMPTY,
112             dateTakenMillisLong = 0L,
113             sizeInBytes = 0,
114             mimeType = "image/jpeg",
115             standardMimeTypeExtension = 0,
116             selectionSource = Telemetry.MediaLocation.MAIN_GRID,
117             mediaItemAlbum = mediaItemAlbum,
118         )
119 
<lambda>null120     private val lazyDataService: Lazy<DataService> = Lazy { TestDataServiceImpl() }
121 
122     private lateinit var lazyEvents: Lazy<Events>
123     private lateinit var eventsDispatched: MutableList<Event>
124     private lateinit var lazyUserMonitor: Lazy<UserMonitor>
125     private lateinit var lazyMediaSelection: Lazy<Selection<Media>>
126     private lateinit var lazyFeatureManager: Lazy<FeatureManager>
127 
setupnull128     private fun setup(testScope: TestScope) {
129         val backgroundScope = testScope.backgroundScope
130         val testScheduler = testScope.testScheduler
131 
132         val photopickerConfigurationStateFlow =
133             provideTestConfigurationFlow(
134                 scope = backgroundScope,
135                 defaultConfiguration = photopickerConfiguration,
136             )
137         val featureManager =
138             FeatureManager(
139                 configuration = photopickerConfigurationStateFlow,
140                 scope = backgroundScope,
141                 prefetchDataService = TestPrefetchDataService(),
142             )
143         lazyFeatureManager = Lazy { featureManager }
144 
145         val events =
146             Events(
147                 scope = backgroundScope,
148                 configuration = photopickerConfigurationStateFlow,
149                 featureManager = featureManager,
150             )
151         lazyEvents = Lazy { events }
152 
153         eventsDispatched = mutableListOf()
154         backgroundScope.launch { events.flow.toList(eventsDispatched) }
155 
156         val mockContext = mock(Context::class.java)
157 
158         val mockUserManager = mock(UserManager::class.java)
159         mockSystemService(mockContext, UserManager::class.java) { mockUserManager }
160 
161         if (SdkLevel.isAtLeastV()) {
162             whenever(
163                 mockUserManager.getUserProperties(any(UserHandle::class.java))
164             ) @JvmSerializableLambda { UserProperties.Builder().build() }
165             whenever(mockUserManager.getUserBadge()) {
166                 InstrumentationRegistry.getInstrumentation()
167                     .context
168                     .resources
169                     .getDrawable(R.drawable.android, /* theme= */ null)
170             }
171             whenever(mockUserManager.getProfileLabel()) { "label" }
172         }
173 
174         whenever(mockContext.packageManager) { mock(PackageManager::class.java) }
175         whenever(mockContext.contentResolver) { mock(ContentResolver::class.java) }
176         whenever(mockContext.createPackageContextAsUser(any(), anyInt(), any())) { mockContext }
177         whenever(mockContext.createContextAsUser(any(UserHandle::class.java), anyInt())) {
178             mockContext
179         }
180 
181         // Get primaryUserHandle: UserHandle
182         val parcel1 = Parcel.obtain()
183         parcel1.writeInt(/* primary user id */ 0)
184         parcel1.setDataPosition(0)
185         val primaryUserHandle = UserHandle(parcel1)
186         parcel1.recycle()
187 
188         val userMonitor =
189             UserMonitor(
190                 context = mockContext,
191                 configuration = photopickerConfigurationStateFlow,
192                 scope = backgroundScope,
193                 dispatcher = StandardTestDispatcher(testScheduler),
194                 processOwnerUserHandle = primaryUserHandle,
195             )
196         lazyUserMonitor = Lazy { userMonitor }
197 
198         val selection =
199             SelectionImpl(
200                 scope = backgroundScope,
201                 configuration = photopickerConfigurationStateFlow,
202                 preSelectedMedia = lazyDataService.get().preSelectionMediaData,
203             )
204         lazyMediaSelection = Lazy { selection }
205     }
206 
207     @Test
<lambda>null208     fun testDispatchPhotopickerExpansionStateChangedEvent_isExpanded() = runTest {
209         // Setup
210         setup(testScope = this)
211 
212         val expectedEvent =
213             Event.LogPhotopickerUIEvent(
214                 dispatcherToken = FeatureToken.CORE.token,
215                 sessionId = sessionId,
216                 packageUid = packageUid,
217                 uiEvent = Telemetry.UiEvent.EXPAND_PICKER,
218             )
219 
220         // Action
221         dispatchPhotopickerExpansionStateChangedEvent(
222             coroutineScope = backgroundScope,
223             lazyEvents = lazyEvents,
224             photopickerConfiguration = photopickerConfiguration,
225             isExpanded = true,
226         )
227         advanceTimeBy(delayTimeMillis = 50)
228 
229         // Assert
230         assertThat(eventsDispatched).contains(expectedEvent)
231     }
232 
233     @Test
<lambda>null234     fun testDispatchPhotopickerExpansionStateChangedEvent_isCollapsed() = runTest {
235         // Setup
236         setup(testScope = this)
237 
238         val expectedEvent =
239             Event.LogPhotopickerUIEvent(
240                 dispatcherToken = FeatureToken.CORE.token,
241                 sessionId = sessionId,
242                 packageUid = packageUid,
243                 uiEvent = Telemetry.UiEvent.COLLAPSE_PICKER,
244             )
245 
246         // Action
247         dispatchPhotopickerExpansionStateChangedEvent(
248             coroutineScope = backgroundScope,
249             lazyEvents = lazyEvents,
250             photopickerConfiguration = photopickerConfiguration,
251             isExpanded = false,
252         )
253         advanceTimeBy(delayTimeMillis = 50)
254 
255         // Assert
256         assertThat(eventsDispatched).contains(expectedEvent)
257     }
258 
259     @Test
<lambda>null260     fun testDispatchReportPhotopickerMediaItemStatusEvent_selected() = runTest {
261         // Setup
262         setup(testScope = this)
263 
264         val expectedEvent =
265             Event.ReportPhotopickerMediaItemStatus(
266                 dispatcherToken = FeatureToken.CORE.token,
267                 sessionId = sessionId,
268                 mediaStatus = Telemetry.MediaStatus.SELECTED,
269                 selectionSource = Telemetry.MediaLocation.MAIN_GRID,
270                 itemPosition = 9999,
271                 selectedAlbum = mediaItemAlbum,
272                 mediaType = Telemetry.MediaType.PHOTO,
273                 cloudOnly = false,
274                 pickerSize = Telemetry.PickerSize.UNSET_PICKER_SIZE,
275             )
276 
277         // Action
278         dispatchReportPhotopickerMediaItemStatusEvent(
279             coroutineScope = backgroundScope,
280             lazyEvents = lazyEvents,
281             photopickerConfiguration = photopickerConfiguration,
282             mediaItem = mediaItem,
283             mediaStatus = Telemetry.MediaStatus.SELECTED,
284         )
285         advanceTimeBy(delayTimeMillis = 50)
286 
287         // Assert
288         assertThat(eventsDispatched).contains(expectedEvent)
289     }
290 
291     @Test
<lambda>null292     fun testDispatchReportPhotopickerMediaItemStatusEvent_unselected() = runTest {
293         // Setup
294         setup(testScope = this)
295 
296         val expectedEvent =
297             Event.ReportPhotopickerMediaItemStatus(
298                 dispatcherToken = FeatureToken.CORE.token,
299                 sessionId = sessionId,
300                 mediaStatus = Telemetry.MediaStatus.UNSELECTED,
301                 selectionSource = Telemetry.MediaLocation.MAIN_GRID,
302                 itemPosition = 9999,
303                 selectedAlbum = mediaItemAlbum,
304                 mediaType = Telemetry.MediaType.PHOTO,
305                 cloudOnly = false,
306                 pickerSize = Telemetry.PickerSize.UNSET_PICKER_SIZE,
307             )
308 
309         // Action
310         dispatchReportPhotopickerMediaItemStatusEvent(
311             coroutineScope = backgroundScope,
312             lazyEvents = lazyEvents,
313             photopickerConfiguration = photopickerConfiguration,
314             mediaItem = mediaItem,
315             mediaStatus = Telemetry.MediaStatus.UNSELECTED,
316         )
317         advanceTimeBy(delayTimeMillis = 50)
318 
319         // Assert
320         assertThat(eventsDispatched).contains(expectedEvent)
321     }
322 
323     @Test
<lambda>null324     fun testDispatchReportPhotopickerSessionInfoEvent() = runTest {
325         // Setup
326         setup(testScope = this)
327 
328         val pickerStatus = Telemetry.PickerStatus.OPENED
329         val pickerCloseMethod = Telemetry.PickerCloseMethod.SWIPE_DOWN
330 
331         val expectedEvent =
332             Event.ReportPhotopickerSessionInfo(
333                 dispatcherToken = FeatureToken.CORE.token,
334                 sessionId = sessionId,
335                 packageUid = packageUid,
336                 pickerSelection = Telemetry.PickerSelection.SINGLE,
337                 cloudProviderUid = -1,
338                 userProfile = Telemetry.UserProfile.PERSONAL,
339                 pickerStatus = pickerStatus,
340                 pickedItemsCount = 0,
341                 pickedItemsSize = 0,
342                 profileSwitchButtonVisible = false,
343                 pickerMode = Telemetry.PickerMode.EMBEDDED_PICKER,
344                 pickerCloseMethod = pickerCloseMethod,
345             )
346 
347         // Action
348         dispatchReportPhotopickerSessionInfoEvent(
349             coroutineScope = backgroundScope,
350             lazyEvents = lazyEvents,
351             photopickerConfiguration = photopickerConfiguration,
352             lazyDataService = lazyDataService,
353             lazyUserMonitor = lazyUserMonitor,
354             lazyMediaSelection = lazyMediaSelection,
355             pickerStatus = pickerStatus,
356             pickerCloseMethod = pickerCloseMethod,
357         )
358         advanceTimeBy(delayTimeMillis = 50)
359 
360         // Assert
361         assertThat(eventsDispatched).contains(expectedEvent)
362     }
363 
364     @Test
testDispatchReportPhotopickerApiInfoEventWithPhotoMimeTypenull365     fun testDispatchReportPhotopickerApiInfoEventWithPhotoMimeType() = runTest {
366         // Setup
367         setup(testScope = this)
368 
369         val mimeTypeList = arrayListOf("image/jpg")
370         val telemetryMimeTypeMapping = Telemetry.MediaType.PHOTO
371 
372         val pickerIntentAction = Telemetry.PickerIntentAction.ACTION_PICK_IMAGES
373         val cloudSearch = lazyFeatureManager.get().isFeatureEnabled(SearchFeature::class.java)
374         val photopickerConfiguration =
375             TestPhotopickerConfiguration.build {
376                 action(value = "")
377                 sessionId(value = sessionId)
378                 callingPackageUid(value = packageUid)
379                 runtimeEnv(value = PhotopickerRuntimeEnv.EMBEDDED)
380                 mimeTypes(mimeTypeList)
381             }
382 
383         val expectedEvent =
384             Event.ReportPhotopickerApiInfo(
385                 dispatcherToken = FeatureToken.CORE.token,
386                 sessionId = sessionId,
387                 pickerIntentAction = pickerIntentAction,
388                 pickerSize = Telemetry.PickerSize.COLLAPSED,
389                 mediaFilter = telemetryMimeTypeMapping,
390                 maxPickedItemsCount = 1,
391                 selectedTab = Telemetry.SelectedTab.UNSET_SELECTED_TAB,
392                 selectedAlbum = Telemetry.SelectedAlbum.UNSET_SELECTED_ALBUM,
393                 isOrderedSelectionSet = false,
394                 isAccentColorSet = false,
395                 isDefaultTabSet = false,
396                 isCloudSearchEnabled = cloudSearch,
397                 isLocalSearchEnabled = false,
398                 isTranscodingRequested = false,
399             )
400 
401         // Action
402         dispatchReportPhotopickerApiInfoEvent(
403             coroutineScope = backgroundScope,
404             lazyEvents = lazyEvents,
405             photopickerConfiguration = photopickerConfiguration,
406             pickerIntentAction = pickerIntentAction,
407             lazyFeatureManager = lazyFeatureManager,
408         )
409         advanceTimeBy(delayTimeMillis = 50)
410 
411         // Assert
412         assertThat(eventsDispatched).contains(expectedEvent)
413         assertThat(expectedEvent.mediaFilter).isEqualTo(telemetryMimeTypeMapping)
414     }
415 
416     @Test
<lambda>null417     fun testDispatchReportPhotopickerApiInfoEventWithVideoMimeType() = runTest {
418         // Setup
419         setup(testScope = this)
420 
421         val mimeTypeList = arrayListOf("video/jpg")
422         val telemetryMimeTypeMapping = Telemetry.MediaType.VIDEO
423 
424         val pickerIntentAction = Telemetry.PickerIntentAction.ACTION_PICK_IMAGES
425         val cloudSearch = lazyFeatureManager.get().isFeatureEnabled(SearchFeature::class.java)
426         val photopickerConfiguration =
427             TestPhotopickerConfiguration.build {
428                 action(value = "")
429                 sessionId(value = sessionId)
430                 callingPackageUid(value = packageUid)
431                 runtimeEnv(value = PhotopickerRuntimeEnv.EMBEDDED)
432                 mimeTypes(mimeTypeList)
433             }
434 
435         val expectedEvent =
436             Event.ReportPhotopickerApiInfo(
437                 dispatcherToken = FeatureToken.CORE.token,
438                 sessionId = sessionId,
439                 pickerIntentAction = pickerIntentAction,
440                 pickerSize = Telemetry.PickerSize.COLLAPSED,
441                 mediaFilter = telemetryMimeTypeMapping,
442                 maxPickedItemsCount = 1,
443                 selectedTab = Telemetry.SelectedTab.UNSET_SELECTED_TAB,
444                 selectedAlbum = Telemetry.SelectedAlbum.UNSET_SELECTED_ALBUM,
445                 isOrderedSelectionSet = false,
446                 isAccentColorSet = false,
447                 isDefaultTabSet = false,
448                 isCloudSearchEnabled = cloudSearch,
449                 isLocalSearchEnabled = false,
450                 isTranscodingRequested = false,
451             )
452 
453         // Action
454         dispatchReportPhotopickerApiInfoEvent(
455             coroutineScope = backgroundScope,
456             lazyEvents = lazyEvents,
457             photopickerConfiguration = photopickerConfiguration,
458             pickerIntentAction = pickerIntentAction,
459             lazyFeatureManager = lazyFeatureManager,
460         )
461         advanceTimeBy(delayTimeMillis = 50)
462 
463         // Assert
464         assertThat(eventsDispatched).contains(expectedEvent)
465         assertThat(expectedEvent.mediaFilter).isEqualTo(telemetryMimeTypeMapping)
466     }
467 
468     @Test
<lambda>null469     fun testDispatchReportPhotopickerApiInfoEventWithBothPhotoAndVideoMimeType() = runTest {
470         // Setup
471         setup(testScope = this)
472 
473         val mimeTypeList = arrayListOf("image/jpg", "video/mp4")
474         val telemetryMimeTypeMapping = Telemetry.MediaType.PHOTO_VIDEO
475 
476         val pickerIntentAction = Telemetry.PickerIntentAction.ACTION_PICK_IMAGES
477         val cloudSearch = lazyFeatureManager.get().isFeatureEnabled(SearchFeature::class.java)
478         val photopickerConfiguration =
479             TestPhotopickerConfiguration.build {
480                 action(value = "")
481                 sessionId(value = sessionId)
482                 callingPackageUid(value = packageUid)
483                 runtimeEnv(value = PhotopickerRuntimeEnv.EMBEDDED)
484                 mimeTypes(mimeTypeList)
485             }
486 
487         val expectedEvent =
488             Event.ReportPhotopickerApiInfo(
489                 dispatcherToken = FeatureToken.CORE.token,
490                 sessionId = sessionId,
491                 pickerIntentAction = pickerIntentAction,
492                 pickerSize = Telemetry.PickerSize.COLLAPSED,
493                 mediaFilter = telemetryMimeTypeMapping,
494                 maxPickedItemsCount = 1,
495                 selectedTab = Telemetry.SelectedTab.UNSET_SELECTED_TAB,
496                 selectedAlbum = Telemetry.SelectedAlbum.UNSET_SELECTED_ALBUM,
497                 isOrderedSelectionSet = false,
498                 isAccentColorSet = false,
499                 isDefaultTabSet = false,
500                 isCloudSearchEnabled = cloudSearch,
501                 isLocalSearchEnabled = false,
502                 isTranscodingRequested = false,
503             )
504 
505         // Action
506         dispatchReportPhotopickerApiInfoEvent(
507             coroutineScope = backgroundScope,
508             lazyEvents = lazyEvents,
509             photopickerConfiguration = photopickerConfiguration,
510             pickerIntentAction = pickerIntentAction,
511             lazyFeatureManager = lazyFeatureManager,
512         )
513         advanceTimeBy(delayTimeMillis = 50)
514 
515         // Assert
516         assertThat(eventsDispatched).contains(expectedEvent)
517         assertThat(expectedEvent.mediaFilter).isEqualTo(telemetryMimeTypeMapping)
518     }
519 
520     @Test
<lambda>null521     fun testDispatchReportPhotopickerApiInfoEventWithDefaultPhotoAndVideoMimeType() = runTest {
522         // Setup
523         setup(testScope = this)
524 
525         val mimeTypeList = arrayListOf("image/*", "video/*")
526         val telemetryMimeTypeMapping = Telemetry.MediaType.PHOTO_VIDEO
527 
528         val pickerIntentAction = Telemetry.PickerIntentAction.ACTION_PICK_IMAGES
529         val cloudSearch = lazyFeatureManager.get().isFeatureEnabled(SearchFeature::class.java)
530         val photopickerConfiguration =
531             TestPhotopickerConfiguration.build {
532                 action(value = "")
533                 sessionId(value = sessionId)
534                 callingPackageUid(value = packageUid)
535                 runtimeEnv(value = PhotopickerRuntimeEnv.EMBEDDED)
536                 mimeTypes(mimeTypeList)
537             }
538 
539         val expectedEvent =
540             Event.ReportPhotopickerApiInfo(
541                 dispatcherToken = FeatureToken.CORE.token,
542                 sessionId = sessionId,
543                 pickerIntentAction = pickerIntentAction,
544                 pickerSize = Telemetry.PickerSize.COLLAPSED,
545                 mediaFilter = telemetryMimeTypeMapping,
546                 maxPickedItemsCount = 1,
547                 selectedTab = Telemetry.SelectedTab.UNSET_SELECTED_TAB,
548                 selectedAlbum = Telemetry.SelectedAlbum.UNSET_SELECTED_ALBUM,
549                 isOrderedSelectionSet = false,
550                 isAccentColorSet = false,
551                 isDefaultTabSet = false,
552                 isCloudSearchEnabled = cloudSearch,
553                 isLocalSearchEnabled = false,
554                 isTranscodingRequested = false,
555             )
556 
557         // Action
558         dispatchReportPhotopickerApiInfoEvent(
559             coroutineScope = backgroundScope,
560             lazyEvents = lazyEvents,
561             photopickerConfiguration = photopickerConfiguration,
562             pickerIntentAction = pickerIntentAction,
563             lazyFeatureManager = lazyFeatureManager,
564         )
565         advanceTimeBy(delayTimeMillis = 50)
566 
567         // Assert
568         assertThat(eventsDispatched).contains(expectedEvent)
569         assertThat(expectedEvent.mediaFilter).isEqualTo(telemetryMimeTypeMapping)
570     }
571 
572     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
573     @Test
<lambda>null574     fun testDispatchReportPickerAppMediaCapabilities() = runTest {
575         // Setup
576         setup(testScope = this)
577 
578         val capabilities =
579             ApplicationMediaCapabilities.Builder().addUnsupportedHdrType(HdrType.HDR10).build()
580 
581         val photopickerConfiguration =
582             TestPhotopickerConfiguration.build {
583                 action(value = "")
584                 sessionId(value = sessionId)
585                 callingPackageUid(value = packageUid)
586                 runtimeEnv(value = PhotopickerRuntimeEnv.EMBEDDED)
587                 appMediaCapabilities(capabilities)
588             }
589 
590         val expectedEvent =
591             Event.ReportPickerAppMediaCapabilities(
592                 dispatcherToken = FeatureToken.CORE.token,
593                 sessionId = sessionId,
594                 supportedHdrTypes = intArrayOf(),
595                 unsupportedHdrTypes = intArrayOf(Telemetry.HdrTypes.HDR10_UNSUPPORTED.type),
596             )
597 
598         // Action
599         dispatchReportPickerAppMediaCapabilities(
600             coroutineScope = backgroundScope,
601             lazyEvents = lazyEvents,
602             photopickerConfiguration = photopickerConfiguration,
603         )
604         advanceTimeBy(delayTimeMillis = 50)
605 
606         // Assert
607         assertThat(eventsDispatched.size).isEqualTo(1)
608         assertThat(eventsDispatched.get(0).toString()).isEqualTo(expectedEvent.toString())
609     }
610 }
611