• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.media.controls.pipeline
18 
19 import android.app.smartspace.SmartspaceAction
20 import android.os.Bundle
21 import android.testing.AndroidTestingRunner
22 import android.testing.TestableLooper
23 import androidx.test.filters.SmallTest
24 import com.android.internal.logging.InstanceId
25 import com.android.systemui.SysuiTestCase
26 import com.android.systemui.broadcast.BroadcastSender
27 import com.android.systemui.media.controls.MediaTestUtils
28 import com.android.systemui.media.controls.models.player.MediaData
29 import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_RESUME
30 import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
31 import com.android.systemui.media.controls.ui.MediaPlayerData
32 import com.android.systemui.media.controls.util.MediaFlags
33 import com.android.systemui.media.controls.util.MediaUiEventLogger
34 import com.android.systemui.settings.UserTracker
35 import com.android.systemui.statusbar.NotificationLockscreenUserManager
36 import com.android.systemui.util.mockito.any
37 import com.android.systemui.util.mockito.eq
38 import com.android.systemui.util.mockito.whenever
39 import com.android.systemui.util.time.FakeSystemClock
40 import com.google.common.truth.Truth.assertThat
41 import java.util.concurrent.Executor
42 import org.junit.Before
43 import org.junit.Test
44 import org.junit.runner.RunWith
45 import org.mockito.ArgumentMatchers.anyBoolean
46 import org.mockito.ArgumentMatchers.anyInt
47 import org.mockito.ArgumentMatchers.anyLong
48 import org.mockito.Mock
49 import org.mockito.Mockito.never
50 import org.mockito.Mockito.reset
51 import org.mockito.Mockito.verify
52 import org.mockito.MockitoAnnotations
53 
54 private const val KEY = "TEST_KEY"
55 private const val KEY_ALT = "TEST_KEY_2"
56 private const val USER_MAIN = 0
57 private const val USER_GUEST = 10
58 private const val PACKAGE = "PKG"
59 private val INSTANCE_ID = InstanceId.fakeInstanceId(123)!!
60 private const val APP_UID = 99
61 private const val SMARTSPACE_KEY = "SMARTSPACE_KEY"
62 private const val SMARTSPACE_PACKAGE = "SMARTSPACE_PKG"
63 private val SMARTSPACE_INSTANCE_ID = InstanceId.fakeInstanceId(456)!!
64 
65 @SmallTest
66 @RunWith(AndroidTestingRunner::class)
67 @TestableLooper.RunWithLooper
68 class MediaDataFilterTest : SysuiTestCase() {
69 
70     @Mock private lateinit var listener: MediaDataManager.Listener
71     @Mock private lateinit var userTracker: UserTracker
72     @Mock private lateinit var broadcastSender: BroadcastSender
73     @Mock private lateinit var mediaDataManager: MediaDataManager
74     @Mock private lateinit var lockscreenUserManager: NotificationLockscreenUserManager
75     @Mock private lateinit var executor: Executor
76     @Mock private lateinit var smartspaceData: SmartspaceMediaData
77     @Mock private lateinit var smartspaceMediaRecommendationItem: SmartspaceAction
78     @Mock private lateinit var logger: MediaUiEventLogger
79     @Mock private lateinit var mediaFlags: MediaFlags
80     @Mock private lateinit var cardAction: SmartspaceAction
81 
82     private lateinit var mediaDataFilter: MediaDataFilter
83     private lateinit var dataMain: MediaData
84     private lateinit var dataGuest: MediaData
85     private val clock = FakeSystemClock()
86 
87     @Before
setupnull88     fun setup() {
89         MockitoAnnotations.initMocks(this)
90         MediaPlayerData.clear()
91         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
92         mediaDataFilter =
93             MediaDataFilter(
94                 context,
95                 userTracker,
96                 broadcastSender,
97                 lockscreenUserManager,
98                 executor,
99                 clock,
100                 logger,
101                 mediaFlags
102             )
103         mediaDataFilter.mediaDataManager = mediaDataManager
104         mediaDataFilter.addListener(listener)
105 
106         // Start all tests as main user
107         setUser(USER_MAIN)
108 
109         // Set up test media data
110         dataMain =
111             MediaTestUtils.emptyMediaData.copy(
112                 userId = USER_MAIN,
113                 packageName = PACKAGE,
114                 instanceId = INSTANCE_ID,
115                 appUid = APP_UID
116             )
117         dataGuest = dataMain.copy(userId = USER_GUEST)
118 
119         whenever(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
120         whenever(smartspaceData.isActive).thenReturn(true)
121         whenever(smartspaceData.isValid()).thenReturn(true)
122         whenever(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE)
123         whenever(smartspaceData.recommendations)
124             .thenReturn(listOf(smartspaceMediaRecommendationItem))
125         whenever(smartspaceData.headphoneConnectionTimeMillis)
126             .thenReturn(clock.currentTimeMillis() - 100)
127         whenever(smartspaceData.instanceId).thenReturn(SMARTSPACE_INSTANCE_ID)
128         whenever(smartspaceData.cardAction).thenReturn(cardAction)
129     }
130 
setUsernull131     private fun setUser(id: Int) {
132         whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
133         whenever(lockscreenUserManager.isCurrentProfile(eq(id))).thenReturn(true)
134         mediaDataFilter.handleUserSwitched(id)
135     }
136 
137     @Test
testOnDataLoadedForCurrentUser_callsListenernull138     fun testOnDataLoadedForCurrentUser_callsListener() {
139         // GIVEN a media for main user
140         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
141 
142         // THEN we should tell the listener
143         verify(listener)
144             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), eq(0), eq(false))
145     }
146 
147     @Test
testOnDataLoadedForGuest_doesNotCallListenernull148     fun testOnDataLoadedForGuest_doesNotCallListener() {
149         // GIVEN a media for guest user
150         mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
151 
152         // THEN we should NOT tell the listener
153         verify(listener, never())
154             .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
155     }
156 
157     @Test
testOnRemovedForCurrent_callsListenernull158     fun testOnRemovedForCurrent_callsListener() {
159         // GIVEN a media was removed for main user
160         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
161         mediaDataFilter.onMediaDataRemoved(KEY)
162 
163         // THEN we should tell the listener
164         verify(listener).onMediaDataRemoved(eq(KEY))
165     }
166 
167     @Test
testOnRemovedForGuest_doesNotCallListenernull168     fun testOnRemovedForGuest_doesNotCallListener() {
169         // GIVEN a media was removed for guest user
170         mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
171         mediaDataFilter.onMediaDataRemoved(KEY)
172 
173         // THEN we should NOT tell the listener
174         verify(listener, never()).onMediaDataRemoved(eq(KEY))
175     }
176 
177     @Test
testOnUserSwitched_removesOldUserControlsnull178     fun testOnUserSwitched_removesOldUserControls() {
179         // GIVEN that we have a media loaded for main user
180         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
181 
182         // and we switch to guest user
183         setUser(USER_GUEST)
184 
185         // THEN we should remove the main user's media
186         verify(listener).onMediaDataRemoved(eq(KEY))
187     }
188 
189     @Test
testOnUserSwitched_addsNewUserControlsnull190     fun testOnUserSwitched_addsNewUserControls() {
191         // GIVEN that we had some media for both users
192         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
193         mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataGuest)
194         reset(listener)
195 
196         // and we switch to guest user
197         setUser(USER_GUEST)
198 
199         // THEN we should add back the guest user media
200         verify(listener)
201             .onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true), eq(0), eq(false))
202 
203         // but not the main user's
204         verify(listener, never())
205             .onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean(), anyInt(), anyBoolean())
206     }
207 
208     @Test
hasAnyMedia_noMediaSet_returnsFalsenull209     fun hasAnyMedia_noMediaSet_returnsFalse() {
210         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
211     }
212 
213     @Test
hasAnyMedia_mediaSet_returnsTruenull214     fun hasAnyMedia_mediaSet_returnsTrue() {
215         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
216 
217         assertThat(mediaDataFilter.hasAnyMedia()).isTrue()
218     }
219 
220     @Test
hasAnyMedia_recommendationSet_returnsFalsenull221     fun hasAnyMedia_recommendationSet_returnsFalse() {
222         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
223 
224         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
225     }
226 
227     @Test
hasAnyMediaOrRecommendation_noMediaSet_returnsFalsenull228     fun hasAnyMediaOrRecommendation_noMediaSet_returnsFalse() {
229         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
230     }
231 
232     @Test
hasAnyMediaOrRecommendation_mediaSet_returnsTruenull233     fun hasAnyMediaOrRecommendation_mediaSet_returnsTrue() {
234         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
235 
236         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
237     }
238 
239     @Test
hasAnyMediaOrRecommendation_recommendationSet_returnsTruenull240     fun hasAnyMediaOrRecommendation_recommendationSet_returnsTrue() {
241         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
242 
243         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
244     }
245 
246     @Test
hasActiveMedia_noMediaSet_returnsFalsenull247     fun hasActiveMedia_noMediaSet_returnsFalse() {
248         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
249     }
250 
251     @Test
hasActiveMedia_inactiveMediaSet_returnsFalsenull252     fun hasActiveMedia_inactiveMediaSet_returnsFalse() {
253         val data = dataMain.copy(active = false)
254         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
255 
256         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
257     }
258 
259     @Test
hasActiveMedia_activeMediaSet_returnsTruenull260     fun hasActiveMedia_activeMediaSet_returnsTrue() {
261         val data = dataMain.copy(active = true)
262         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
263 
264         assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
265     }
266 
267     @Test
hasActiveMediaOrRecommendation_nothingSet_returnsFalsenull268     fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() {
269         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
270     }
271 
272     @Test
hasActiveMediaOrRecommendation_inactiveMediaSet_returnsFalsenull273     fun hasActiveMediaOrRecommendation_inactiveMediaSet_returnsFalse() {
274         val data = dataMain.copy(active = false)
275         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
276 
277         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
278     }
279 
280     @Test
hasActiveMediaOrRecommendation_activeMediaSet_returnsTruenull281     fun hasActiveMediaOrRecommendation_activeMediaSet_returnsTrue() {
282         val data = dataMain.copy(active = true)
283         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
284 
285         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
286     }
287 
288     @Test
hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalsenull289     fun hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalse() {
290         whenever(smartspaceData.isActive).thenReturn(false)
291         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
292 
293         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
294     }
295 
296     @Test
hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalsenull297     fun hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalse() {
298         whenever(smartspaceData.isValid()).thenReturn(false)
299         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
300 
301         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
302     }
303 
304     @Test
hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTruenull305     fun hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTrue() {
306         whenever(smartspaceData.isActive).thenReturn(true)
307         whenever(smartspaceData.isValid()).thenReturn(true)
308         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
309 
310         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
311     }
312 
313     @Test
testHasAnyMediaOrRecommendation_onlyCurrentUsernull314     fun testHasAnyMediaOrRecommendation_onlyCurrentUser() {
315         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
316 
317         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataGuest)
318         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
319         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
320     }
321 
322     @Test
testHasActiveMediaOrRecommendation_onlyCurrentUsernull323     fun testHasActiveMediaOrRecommendation_onlyCurrentUser() {
324         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
325         val data = dataGuest.copy(active = true)
326 
327         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
328         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
329         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
330     }
331 
332     @Test
testOnNotificationRemoved_doesntHaveMedianull333     fun testOnNotificationRemoved_doesntHaveMedia() {
334         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
335         mediaDataFilter.onMediaDataRemoved(KEY)
336         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
337         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
338     }
339 
340     @Test
testOnSwipeToDismiss_setsTimedOutnull341     fun testOnSwipeToDismiss_setsTimedOut() {
342         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
343         mediaDataFilter.onSwipeToDismiss()
344 
345         verify(mediaDataManager).setTimedOut(eq(KEY), eq(true), eq(true))
346     }
347 
348     @Test
testOnSmartspaceMediaDataLoaded_noMedia_activeValidRec_prioritizesSmartspacenull349     fun testOnSmartspaceMediaDataLoaded_noMedia_activeValidRec_prioritizesSmartspace() {
350         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
351 
352         verify(listener)
353             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
354         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
355         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
356         verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
357         verify(logger, never()).logRecommendationActivated(any(), any(), any())
358     }
359 
360     @Test
testOnSmartspaceMediaDataLoaded_noMedia_inactiveRec_showsNothingnull361     fun testOnSmartspaceMediaDataLoaded_noMedia_inactiveRec_showsNothing() {
362         whenever(smartspaceData.isActive).thenReturn(false)
363 
364         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
365 
366         verify(listener, never())
367             .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
368         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
369         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
370         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
371         verify(logger, never()).logRecommendationAdded(any(), any())
372         verify(logger, never()).logRecommendationActivated(any(), any(), any())
373     }
374 
375     @Test
testOnSmartspaceMediaDataLoaded_noRecentMedia_activeValidRec_prioritizesSmartspacenull376     fun testOnSmartspaceMediaDataLoaded_noRecentMedia_activeValidRec_prioritizesSmartspace() {
377         val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
378         mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
379         clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
380         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
381 
382         verify(listener)
383             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
384         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
385         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
386         verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
387         verify(logger, never()).logRecommendationActivated(any(), any(), any())
388     }
389 
390     @Test
testOnSmartspaceMediaDataLoaded_noRecentMedia_inactiveRec_showsNothingnull391     fun testOnSmartspaceMediaDataLoaded_noRecentMedia_inactiveRec_showsNothing() {
392         whenever(smartspaceData.isActive).thenReturn(false)
393 
394         val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
395         mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
396         clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
397         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
398 
399         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
400         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
401         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
402         verify(logger, never()).logRecommendationAdded(any(), any())
403         verify(logger, never()).logRecommendationActivated(any(), any(), any())
404     }
405 
406     @Test
testOnSmartspaceMediaDataLoaded_hasRecentMedia_inactiveRec_showsNothingnull407     fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_inactiveRec_showsNothing() {
408         whenever(smartspaceData.isActive).thenReturn(false)
409 
410         // WHEN we have media that was recently played, but not currently active
411         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
412         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
413         verify(listener)
414             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
415 
416         // AND we get a smartspace signal
417         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
418 
419         // THEN we should tell listeners to treat the media as not active instead
420         verify(listener, never())
421             .onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(), anyInt(), anyBoolean())
422         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
423         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
424         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
425         verify(logger, never()).logRecommendationAdded(any(), any())
426         verify(logger, never()).logRecommendationActivated(any(), any(), any())
427     }
428 
429     @Test
testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedianull430     fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedia() {
431         whenever(smartspaceData.isValid()).thenReturn(false)
432 
433         // WHEN we have media that was recently played, but not currently active
434         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
435         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
436         verify(listener)
437             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
438 
439         // AND we get a smartspace signal
440         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
441 
442         // THEN we should tell listeners to treat the media as active instead
443         val dataCurrentAndActive = dataCurrent.copy(active = true)
444         verify(listener)
445             .onMediaDataLoaded(
446                 eq(KEY),
447                 eq(KEY),
448                 eq(dataCurrentAndActive),
449                 eq(true),
450                 eq(100),
451                 eq(true)
452             )
453         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
454         // Smartspace update shouldn't be propagated for the empty rec list.
455         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
456         verify(logger, never()).logRecommendationAdded(any(), any())
457         verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
458     }
459 
460     @Test
testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeValidRec_usesBothnull461     fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeValidRec_usesBoth() {
462         // WHEN we have media that was recently played, but not currently active
463         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
464         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
465         verify(listener)
466             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
467 
468         // AND we get a smartspace signal
469         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
470 
471         // THEN we should tell listeners to treat the media as active instead
472         val dataCurrentAndActive = dataCurrent.copy(active = true)
473         verify(listener)
474             .onMediaDataLoaded(
475                 eq(KEY),
476                 eq(KEY),
477                 eq(dataCurrentAndActive),
478                 eq(true),
479                 eq(100),
480                 eq(true)
481             )
482         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
483         // Smartspace update should also be propagated but not prioritized.
484         verify(listener)
485             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
486         verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
487         verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
488     }
489 
490     @Test
testOnSmartspaceMediaDataRemoved_usedSmartspace_clearsSmartspacenull491     fun testOnSmartspaceMediaDataRemoved_usedSmartspace_clearsSmartspace() {
492         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
493         mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
494 
495         verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
496         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
497         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
498     }
499 
500     @Test
testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBothnull501     fun testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() {
502         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
503         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
504         verify(listener)
505             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
506 
507         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
508 
509         val dataCurrentAndActive = dataCurrent.copy(active = true)
510         verify(listener)
511             .onMediaDataLoaded(
512                 eq(KEY),
513                 eq(KEY),
514                 eq(dataCurrentAndActive),
515                 eq(true),
516                 eq(100),
517                 eq(true)
518             )
519 
520         mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
521 
522         verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
523         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
524         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
525     }
526 
527     @Test
testOnSmartspaceLoaded_persistentEnabled_isInactive_notifiesListenersnull528     fun testOnSmartspaceLoaded_persistentEnabled_isInactive_notifiesListeners() {
529         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
530         whenever(smartspaceData.isActive).thenReturn(false)
531 
532         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
533 
534         verify(listener)
535             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
536         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
537         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
538     }
539 
540     @Test
testOnSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactivenull541     fun testOnSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactive() {
542         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
543         whenever(smartspaceData.isActive).thenReturn(false)
544 
545         // If there is media that was recently played but inactive
546         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
547         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
548         verify(listener)
549             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
550 
551         // And an inactive recommendation is loaded
552         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
553 
554         // Smartspace is loaded but the media stays inactive
555         verify(listener)
556             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
557         verify(listener, never())
558             .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
559         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
560         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
561     }
562 
563     @Test
testOnSwipeToDismiss_persistentEnabled_recommendationSetInactivenull564     fun testOnSwipeToDismiss_persistentEnabled_recommendationSetInactive() {
565         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
566 
567         val data =
568             EMPTY_SMARTSPACE_MEDIA_DATA.copy(
569                 targetId = SMARTSPACE_KEY,
570                 isActive = true,
571                 packageName = SMARTSPACE_PACKAGE,
572                 recommendations = listOf(smartspaceMediaRecommendationItem),
573             )
574         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data)
575         mediaDataFilter.onSwipeToDismiss()
576 
577         verify(mediaDataManager).setRecommendationInactive(eq(SMARTSPACE_KEY))
578         verify(mediaDataManager, never())
579             .dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong())
580     }
581 
582     @Test
testSmartspaceLoaded_shouldTriggerResume_doesTriggernull583     fun testSmartspaceLoaded_shouldTriggerResume_doesTrigger() {
584         // WHEN we have media that was recently played, but not currently active
585         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
586         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
587         verify(listener)
588             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
589 
590         // AND we get a smartspace signal with extra to trigger resume
591         val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, true) }
592         whenever(cardAction.extras).thenReturn(extras)
593         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
594 
595         // THEN we should tell listeners to treat the media as active instead
596         val dataCurrentAndActive = dataCurrent.copy(active = true)
597         verify(listener)
598             .onMediaDataLoaded(
599                 eq(KEY),
600                 eq(KEY),
601                 eq(dataCurrentAndActive),
602                 eq(true),
603                 eq(100),
604                 eq(true)
605             )
606         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
607         // And send the smartspace data, but not prioritized
608         verify(listener)
609             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
610     }
611 
612     @Test
testSmartspaceLoaded_notShouldTriggerResume_doesNotTriggernull613     fun testSmartspaceLoaded_notShouldTriggerResume_doesNotTrigger() {
614         // WHEN we have media that was recently played, but not currently active
615         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
616         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
617         verify(listener)
618             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
619 
620         // AND we get a smartspace signal with extra to not trigger resume
621         val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, false) }
622         whenever(cardAction.extras).thenReturn(extras)
623         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
624 
625         // THEN listeners are not updated to show media
626         verify(listener, never())
627             .onMediaDataLoaded(eq(KEY), eq(KEY), any(), eq(true), eq(100), eq(true))
628         // But the smartspace update is still propagated
629         verify(listener)
630             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
631     }
632 }
633