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.domain.pipeline
18 
19 import android.app.smartspace.SmartspaceAction
20 import android.os.Bundle
21 import android.testing.TestableLooper
22 import androidx.test.ext.junit.runners.AndroidJUnit4
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.shared.model.EXTRA_KEY_TRIGGER_RESUME
29 import com.android.systemui.media.controls.shared.model.MediaData
30 import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
31 import com.android.systemui.media.controls.ui.controller.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.time.FakeSystemClock
37 import com.google.common.truth.Truth.assertThat
38 import java.util.concurrent.Executor
39 import org.junit.Before
40 import org.junit.Test
41 import org.junit.runner.RunWith
42 import org.mockito.ArgumentMatchers.anyBoolean
43 import org.mockito.ArgumentMatchers.anyInt
44 import org.mockito.ArgumentMatchers.anyLong
45 import org.mockito.Mock
46 import org.mockito.Mockito.never
47 import org.mockito.Mockito.reset
48 import org.mockito.Mockito.verify
49 import org.mockito.MockitoAnnotations
50 import org.mockito.kotlin.any
51 import org.mockito.kotlin.eq
52 import org.mockito.kotlin.whenever
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 PRIVATE_PROFILE = 12
59 private const val PACKAGE = "PKG"
60 private val INSTANCE_ID = InstanceId.fakeInstanceId(123)!!
61 private const val APP_UID = 99
62 private const val SMARTSPACE_KEY = "SMARTSPACE_KEY"
63 private const val SMARTSPACE_PACKAGE = "SMARTSPACE_PKG"
64 private val SMARTSPACE_INSTANCE_ID = InstanceId.fakeInstanceId(456)!!
65 
66 @SmallTest
67 @RunWith(AndroidJUnit4::class)
68 @TestableLooper.RunWithLooper
69 class LegacyMediaDataFilterImplTest : SysuiTestCase() {
70 
71     @Mock private lateinit var listener: MediaDataManager.Listener
72     @Mock private lateinit var userTracker: UserTracker
73     @Mock private lateinit var broadcastSender: BroadcastSender
74     @Mock private lateinit var mediaDataManager: MediaDataManager
75     @Mock private lateinit var lockscreenUserManager: NotificationLockscreenUserManager
76     @Mock private lateinit var executor: Executor
77     @Mock private lateinit var smartspaceData: SmartspaceMediaData
78     @Mock private lateinit var smartspaceMediaRecommendationItem: SmartspaceAction
79     @Mock private lateinit var logger: MediaUiEventLogger
80     @Mock private lateinit var mediaFlags: MediaFlags
81     @Mock private lateinit var cardAction: SmartspaceAction
82 
83     private lateinit var mediaDataFilter: LegacyMediaDataFilterImpl
84     private lateinit var dataMain: MediaData
85     private lateinit var dataGuest: MediaData
86     private lateinit var dataPrivateProfile: MediaData
87     private val clock = FakeSystemClock()
88 
89     @Before
setupnull90     fun setup() {
91         MockitoAnnotations.initMocks(this)
92         MediaPlayerData.clear()
93         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
94         mediaDataFilter =
95             LegacyMediaDataFilterImpl(
96                 context,
97                 userTracker,
98                 broadcastSender,
99                 lockscreenUserManager,
100                 executor,
101                 clock,
102                 logger,
103                 mediaFlags
104             )
105         mediaDataFilter.mediaDataManager = mediaDataManager
106         mediaDataFilter.addListener(listener)
107 
108         // Start all tests as main user
109         setUser(USER_MAIN)
110 
111         // Set up test media data
112         dataMain =
113             MediaTestUtils.emptyMediaData.copy(
114                 userId = USER_MAIN,
115                 packageName = PACKAGE,
116                 instanceId = INSTANCE_ID,
117                 appUid = APP_UID
118             )
119         dataGuest = dataMain.copy(userId = USER_GUEST)
120         dataPrivateProfile = dataMain.copy(userId = PRIVATE_PROFILE)
121 
122         whenever(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
123         whenever(smartspaceData.isActive).thenReturn(true)
124         whenever(smartspaceData.isValid()).thenReturn(true)
125         whenever(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE)
126         whenever(smartspaceData.recommendations)
127             .thenReturn(listOf(smartspaceMediaRecommendationItem))
128         whenever(smartspaceData.headphoneConnectionTimeMillis)
129             .thenReturn(clock.currentTimeMillis() - 100)
130         whenever(smartspaceData.instanceId).thenReturn(SMARTSPACE_INSTANCE_ID)
131         whenever(smartspaceData.cardAction).thenReturn(cardAction)
132     }
133 
setUsernull134     private fun setUser(id: Int) {
135         whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
136         whenever(lockscreenUserManager.isProfileAvailable(anyInt())).thenReturn(false)
137         whenever(lockscreenUserManager.isCurrentProfile(eq(id))).thenReturn(true)
138         whenever(lockscreenUserManager.isProfileAvailable(eq(id))).thenReturn(true)
139         whenever(lockscreenUserManager.isProfileAvailable(eq(PRIVATE_PROFILE))).thenReturn(true)
140         mediaDataFilter.handleUserSwitched()
141     }
142 
setPrivateProfileUnavailablenull143     private fun setPrivateProfileUnavailable() {
144         whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
145         whenever(lockscreenUserManager.isCurrentProfile(eq(USER_MAIN))).thenReturn(true)
146         whenever(lockscreenUserManager.isCurrentProfile(eq(PRIVATE_PROFILE))).thenReturn(true)
147         whenever(lockscreenUserManager.isProfileAvailable(eq(PRIVATE_PROFILE))).thenReturn(false)
148         mediaDataFilter.handleProfileChanged()
149     }
150 
151     @Test
testOnDataLoadedForCurrentUser_callsListenernull152     fun testOnDataLoadedForCurrentUser_callsListener() {
153         // GIVEN a media for main user
154         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
155 
156         // THEN we should tell the listener
157         verify(listener)
158             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), eq(0), eq(false))
159     }
160 
161     @Test
testOnDataLoadedForGuest_doesNotCallListenernull162     fun testOnDataLoadedForGuest_doesNotCallListener() {
163         // GIVEN a media for guest user
164         mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
165 
166         // THEN we should NOT tell the listener
167         verify(listener, never())
168             .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
169     }
170 
171     @Test
testOnRemovedForCurrent_callsListenernull172     fun testOnRemovedForCurrent_callsListener() {
173         // GIVEN a media was removed for main user
174         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
175         mediaDataFilter.onMediaDataRemoved(KEY, false)
176 
177         // THEN we should tell the listener
178         verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
179     }
180 
181     @Test
testOnRemovedForGuest_doesNotCallListenernull182     fun testOnRemovedForGuest_doesNotCallListener() {
183         // GIVEN a media was removed for guest user
184         mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
185         mediaDataFilter.onMediaDataRemoved(KEY, false)
186 
187         // THEN we should NOT tell the listener
188         verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
189     }
190 
191     @Test
testOnUserSwitched_removesOldUserControlsnull192     fun testOnUserSwitched_removesOldUserControls() {
193         // GIVEN that we have a media loaded for main user
194         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
195 
196         // and we switch to guest user
197         setUser(USER_GUEST)
198 
199         // THEN we should remove the main user's media
200         verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
201     }
202 
203     @Test
testOnUserSwitched_addsNewUserControlsnull204     fun testOnUserSwitched_addsNewUserControls() {
205         // GIVEN that we had some media for both users
206         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
207         mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataGuest)
208         reset(listener)
209 
210         // and we switch to guest user
211         setUser(USER_GUEST)
212 
213         // THEN we should add back the guest user media
214         verify(listener)
215             .onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true), eq(0), eq(false))
216 
217         // but not the main user's
218         verify(listener, never())
219             .onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean(), anyInt(), anyBoolean())
220     }
221 
222     @Test
testOnProfileChanged_profileUnavailable_loadControlsnull223     fun testOnProfileChanged_profileUnavailable_loadControls() {
224         // GIVEN that we had some media for both profiles
225         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
226         mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataPrivateProfile)
227         reset(listener)
228 
229         // and we change profile status
230         setPrivateProfileUnavailable()
231 
232         // THEN we should add the private profile media
233         verify(listener).onMediaDataRemoved(eq(KEY_ALT), eq(false))
234     }
235 
236     @Test
hasAnyMedia_noMediaSet_returnsFalsenull237     fun hasAnyMedia_noMediaSet_returnsFalse() {
238         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
239     }
240 
241     @Test
hasAnyMedia_mediaSet_returnsTruenull242     fun hasAnyMedia_mediaSet_returnsTrue() {
243         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
244 
245         assertThat(mediaDataFilter.hasAnyMedia()).isTrue()
246     }
247 
248     @Test
hasAnyMedia_recommendationSet_returnsFalsenull249     fun hasAnyMedia_recommendationSet_returnsFalse() {
250         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
251 
252         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
253     }
254 
255     @Test
hasAnyMediaOrRecommendation_noMediaSet_returnsFalsenull256     fun hasAnyMediaOrRecommendation_noMediaSet_returnsFalse() {
257         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
258     }
259 
260     @Test
hasAnyMediaOrRecommendation_mediaSet_returnsTruenull261     fun hasAnyMediaOrRecommendation_mediaSet_returnsTrue() {
262         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
263 
264         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
265     }
266 
267     @Test
hasAnyMediaOrRecommendation_recommendationSet_returnsTruenull268     fun hasAnyMediaOrRecommendation_recommendationSet_returnsTrue() {
269         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
270 
271         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
272     }
273 
274     @Test
hasActiveMedia_noMediaSet_returnsFalsenull275     fun hasActiveMedia_noMediaSet_returnsFalse() {
276         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
277     }
278 
279     @Test
hasActiveMedia_inactiveMediaSet_returnsFalsenull280     fun hasActiveMedia_inactiveMediaSet_returnsFalse() {
281         val data = dataMain.copy(active = false)
282         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
283 
284         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
285     }
286 
287     @Test
hasActiveMedia_activeMediaSet_returnsTruenull288     fun hasActiveMedia_activeMediaSet_returnsTrue() {
289         val data = dataMain.copy(active = true)
290         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
291 
292         assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
293     }
294 
295     @Test
hasActiveMediaOrRecommendation_nothingSet_returnsFalsenull296     fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() {
297         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
298     }
299 
300     @Test
hasActiveMediaOrRecommendation_inactiveMediaSet_returnsFalsenull301     fun hasActiveMediaOrRecommendation_inactiveMediaSet_returnsFalse() {
302         val data = dataMain.copy(active = false)
303         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
304 
305         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
306     }
307 
308     @Test
hasActiveMediaOrRecommendation_activeMediaSet_returnsTruenull309     fun hasActiveMediaOrRecommendation_activeMediaSet_returnsTrue() {
310         val data = dataMain.copy(active = true)
311         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
312 
313         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
314     }
315 
316     @Test
hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalsenull317     fun hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalse() {
318         whenever(smartspaceData.isActive).thenReturn(false)
319         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
320 
321         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
322     }
323 
324     @Test
hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalsenull325     fun hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalse() {
326         whenever(smartspaceData.isValid()).thenReturn(false)
327         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
328 
329         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
330     }
331 
332     @Test
hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTruenull333     fun hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTrue() {
334         whenever(smartspaceData.isActive).thenReturn(true)
335         whenever(smartspaceData.isValid()).thenReturn(true)
336         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
337 
338         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
339     }
340 
341     @Test
testHasAnyMediaOrRecommendation_onlyCurrentUsernull342     fun testHasAnyMediaOrRecommendation_onlyCurrentUser() {
343         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
344 
345         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataGuest)
346         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
347         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
348     }
349 
350     @Test
testHasActiveMediaOrRecommendation_onlyCurrentUsernull351     fun testHasActiveMediaOrRecommendation_onlyCurrentUser() {
352         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
353         val data = dataGuest.copy(active = true)
354 
355         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
356         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
357         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
358     }
359 
360     @Test
testOnNotificationRemoved_doesntHaveMedianull361     fun testOnNotificationRemoved_doesntHaveMedia() {
362         mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
363         mediaDataFilter.onMediaDataRemoved(KEY, false)
364         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
365         assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
366     }
367 
368     @Test
testOnSwipeToDismiss_setsTimedOutnull369     fun testOnSwipeToDismiss_setsTimedOut() {
370         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
371         mediaDataFilter.onSwipeToDismiss()
372 
373         verify(mediaDataManager).setInactive(eq(KEY), eq(true), eq(true))
374     }
375 
376     @Test
testOnSmartspaceMediaDataLoaded_noMedia_activeValidRec_prioritizesSmartspacenull377     fun testOnSmartspaceMediaDataLoaded_noMedia_activeValidRec_prioritizesSmartspace() {
378         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
379 
380         verify(listener)
381             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
382         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
383         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
384         verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
385         verify(logger, never()).logRecommendationActivated(any(), any(), any())
386     }
387 
388     @Test
testOnSmartspaceMediaDataLoaded_noMedia_inactiveRec_showsNothingnull389     fun testOnSmartspaceMediaDataLoaded_noMedia_inactiveRec_showsNothing() {
390         whenever(smartspaceData.isActive).thenReturn(false)
391 
392         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
393 
394         verify(listener, never())
395             .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
396         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
397         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
398         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
399         verify(logger, never()).logRecommendationAdded(any(), any())
400         verify(logger, never()).logRecommendationActivated(any(), any(), any())
401     }
402 
403     @Test
testOnSmartspaceMediaDataLoaded_noRecentMedia_activeValidRec_prioritizesSmartspacenull404     fun testOnSmartspaceMediaDataLoaded_noRecentMedia_activeValidRec_prioritizesSmartspace() {
405         val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
406         mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
407         clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
408         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
409 
410         verify(listener)
411             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
412         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
413         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
414         verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
415         verify(logger, never()).logRecommendationActivated(any(), any(), any())
416     }
417 
418     @Test
testOnSmartspaceMediaDataLoaded_noRecentMedia_inactiveRec_showsNothingnull419     fun testOnSmartspaceMediaDataLoaded_noRecentMedia_inactiveRec_showsNothing() {
420         whenever(smartspaceData.isActive).thenReturn(false)
421 
422         val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
423         mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
424         clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
425         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
426 
427         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
428         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
429         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
430         verify(logger, never()).logRecommendationAdded(any(), any())
431         verify(logger, never()).logRecommendationActivated(any(), any(), any())
432     }
433 
434     @Test
testOnSmartspaceMediaDataLoaded_hasRecentMedia_inactiveRec_showsNothingnull435     fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_inactiveRec_showsNothing() {
436         whenever(smartspaceData.isActive).thenReturn(false)
437 
438         // WHEN we have media that was recently played, but not currently active
439         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
440         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
441         verify(listener)
442             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
443 
444         // AND we get a smartspace signal
445         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
446 
447         // THEN we should tell listeners to treat the media as not active instead
448         verify(listener, never())
449             .onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(), anyInt(), anyBoolean())
450         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
451         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
452         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
453         verify(logger, never()).logRecommendationAdded(any(), any())
454         verify(logger, never()).logRecommendationActivated(any(), any(), any())
455     }
456 
457     @Test
testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedianull458     fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedia() {
459         whenever(smartspaceData.isValid()).thenReturn(false)
460 
461         // WHEN we have media that was recently played, but not currently active
462         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
463         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
464         verify(listener)
465             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
466 
467         // AND we get a smartspace signal
468         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
469 
470         // THEN we should tell listeners to treat the media as active instead
471         val dataCurrentAndActive = dataCurrent.copy(active = true)
472         verify(listener)
473             .onMediaDataLoaded(
474                 eq(KEY),
475                 eq(KEY),
476                 eq(dataCurrentAndActive),
477                 eq(true),
478                 eq(100),
479                 eq(true)
480             )
481         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
482         // Smartspace update shouldn't be propagated for the empty rec list.
483         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
484         verify(logger, never()).logRecommendationAdded(any(), any())
485         verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
486     }
487 
488     @Test
testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeValidRec_usesBothnull489     fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeValidRec_usesBoth() {
490         // WHEN we have media that was recently played, but not currently active
491         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
492         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
493         verify(listener)
494             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
495 
496         // AND we get a smartspace signal
497         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
498 
499         // THEN we should tell listeners to treat the media as active instead
500         val dataCurrentAndActive = dataCurrent.copy(active = true)
501         verify(listener)
502             .onMediaDataLoaded(
503                 eq(KEY),
504                 eq(KEY),
505                 eq(dataCurrentAndActive),
506                 eq(true),
507                 eq(100),
508                 eq(true)
509             )
510         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
511         // Smartspace update should also be propagated but not prioritized.
512         verify(listener)
513             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
514         verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
515         verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
516     }
517 
518     @Test
testOnSmartspaceMediaDataRemoved_usedSmartspace_clearsSmartspacenull519     fun testOnSmartspaceMediaDataRemoved_usedSmartspace_clearsSmartspace() {
520         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
521         mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
522 
523         verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
524         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
525         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
526     }
527 
528     @Test
testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBothnull529     fun testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() {
530         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
531         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
532         verify(listener)
533             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
534 
535         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
536 
537         val dataCurrentAndActive = dataCurrent.copy(active = true)
538         verify(listener)
539             .onMediaDataLoaded(
540                 eq(KEY),
541                 eq(KEY),
542                 eq(dataCurrentAndActive),
543                 eq(true),
544                 eq(100),
545                 eq(true)
546             )
547 
548         mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
549 
550         verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
551         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
552         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
553     }
554 
555     @Test
testOnSmartspaceLoaded_persistentEnabled_isInactive_notifiesListenersnull556     fun testOnSmartspaceLoaded_persistentEnabled_isInactive_notifiesListeners() {
557         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
558         whenever(smartspaceData.isActive).thenReturn(false)
559 
560         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
561 
562         verify(listener)
563             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
564         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
565         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
566     }
567 
568     @Test
testOnSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactivenull569     fun testOnSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactive() {
570         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
571         whenever(smartspaceData.isActive).thenReturn(false)
572 
573         // If there is media that was recently played but inactive
574         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
575         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
576         verify(listener)
577             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
578 
579         // And an inactive recommendation is loaded
580         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
581 
582         // Smartspace is loaded but the media stays inactive
583         verify(listener)
584             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
585         verify(listener, never())
586             .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
587         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
588         assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
589     }
590 
591     @Test
testOnSwipeToDismiss_persistentEnabled_recommendationSetInactivenull592     fun testOnSwipeToDismiss_persistentEnabled_recommendationSetInactive() {
593         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
594 
595         val data =
596             EMPTY_SMARTSPACE_MEDIA_DATA.copy(
597                 targetId = SMARTSPACE_KEY,
598                 isActive = true,
599                 packageName = SMARTSPACE_PACKAGE,
600                 recommendations = listOf(smartspaceMediaRecommendationItem),
601             )
602         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data)
603         mediaDataFilter.onSwipeToDismiss()
604 
605         verify(mediaDataManager).setRecommendationInactive(eq(SMARTSPACE_KEY))
606         verify(mediaDataManager, never())
607             .dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong())
608     }
609 
610     @Test
testSmartspaceLoaded_shouldTriggerResume_doesTriggernull611     fun testSmartspaceLoaded_shouldTriggerResume_doesTrigger() {
612         // WHEN we have media that was recently played, but not currently active
613         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
614         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
615         verify(listener)
616             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
617 
618         // AND we get a smartspace signal with extra to trigger resume
619         val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, true) }
620         whenever(cardAction.extras).thenReturn(extras)
621         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
622 
623         // THEN we should tell listeners to treat the media as active instead
624         val dataCurrentAndActive = dataCurrent.copy(active = true)
625         verify(listener)
626             .onMediaDataLoaded(
627                 eq(KEY),
628                 eq(KEY),
629                 eq(dataCurrentAndActive),
630                 eq(true),
631                 eq(100),
632                 eq(true)
633             )
634         assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
635         // And send the smartspace data, but not prioritized
636         verify(listener)
637             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
638     }
639 
640     @Test
testSmartspaceLoaded_notShouldTriggerResume_doesNotTriggernull641     fun testSmartspaceLoaded_notShouldTriggerResume_doesNotTrigger() {
642         // WHEN we have media that was recently played, but not currently active
643         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
644         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
645         verify(listener)
646             .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
647 
648         // AND we get a smartspace signal with extra to not trigger resume
649         val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, false) }
650         whenever(cardAction.extras).thenReturn(extras)
651         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
652 
653         // THEN listeners are not updated to show media
654         verify(listener, never())
655             .onMediaDataLoaded(eq(KEY), eq(KEY), any(), eq(true), eq(100), eq(true))
656         // But the smartspace update is still propagated
657         verify(listener)
658             .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
659     }
660 }
661