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