1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.media.controls.domain.pipeline 18 19 import android.R 20 import android.app.smartspace.SmartspaceAction 21 import android.graphics.drawable.Icon 22 import android.os.Bundle 23 import android.os.Process 24 import android.testing.TestableLooper 25 import androidx.test.ext.junit.runners.AndroidJUnit4 26 import androidx.test.filters.SmallTest 27 import com.android.internal.logging.InstanceId 28 import com.android.systemui.SysuiTestCase 29 import com.android.systemui.broadcast.BroadcastSender 30 import com.android.systemui.coroutines.collectLastValue 31 import com.android.systemui.media.controls.MediaTestUtils 32 import com.android.systemui.media.controls.data.repository.MediaFilterRepository 33 import com.android.systemui.media.controls.data.repository.mediaFilterRepository 34 import com.android.systemui.media.controls.shared.mockMediaLogger 35 import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_RESUME 36 import com.android.systemui.media.controls.shared.model.MediaCommonModel 37 import com.android.systemui.media.controls.shared.model.MediaData 38 import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel 39 import com.android.systemui.media.controls.shared.model.SmartspaceMediaData 40 import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel 41 import com.android.systemui.media.controls.ui.controller.MediaPlayerData 42 import com.android.systemui.media.controls.util.MediaFlags 43 import com.android.systemui.media.controls.util.MediaSmartspaceLogger 44 import com.android.systemui.media.controls.util.MediaUiEventLogger 45 import com.android.systemui.media.controls.util.SmallHash 46 import com.android.systemui.media.controls.util.mediaSmartspaceLogger 47 import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger 48 import com.android.systemui.settings.UserTracker 49 import com.android.systemui.statusbar.NotificationLockscreenUserManager 50 import com.android.systemui.testKosmos 51 import com.android.systemui.util.time.FakeSystemClock 52 import com.google.common.truth.Truth.assertThat 53 import java.util.concurrent.Executor 54 import kotlinx.coroutines.ExperimentalCoroutinesApi 55 import kotlinx.coroutines.test.TestScope 56 import kotlinx.coroutines.test.runCurrent 57 import kotlinx.coroutines.test.runTest 58 import org.junit.Before 59 import org.junit.Test 60 import org.junit.runner.RunWith 61 import org.mockito.ArgumentMatchers.anyBoolean 62 import org.mockito.ArgumentMatchers.anyInt 63 import org.mockito.ArgumentMatchers.anyLong 64 import org.mockito.ArgumentMatchers.anyString 65 import org.mockito.Mock 66 import org.mockito.Mockito.never 67 import org.mockito.Mockito.reset 68 import org.mockito.Mockito.verify 69 import org.mockito.MockitoAnnotations 70 import org.mockito.kotlin.any 71 import org.mockito.kotlin.eq 72 import org.mockito.kotlin.whenever 73 74 private const val KEY = "TEST_KEY" 75 private const val KEY_ALT = "TEST_KEY_2" 76 private const val USER_MAIN = 0 77 private const val USER_GUEST = 10 78 private const val PRIVATE_PROFILE = 12 79 private const val PACKAGE = "PKG" 80 private val INSTANCE_ID = InstanceId.fakeInstanceId(123)!! 81 private val INSTANCE_ID_GUEST = InstanceId.fakeInstanceId(321)!! 82 private const val APP_UID = 99 83 private const val SMARTSPACE_KEY = "SMARTSPACE_KEY" 84 private const val SMARTSPACE_PACKAGE = "SMARTSPACE_PKG" 85 private val SMARTSPACE_INSTANCE_ID = InstanceId.fakeInstanceId(456)!! 86 87 @ExperimentalCoroutinesApi 88 @SmallTest 89 @RunWith(AndroidJUnit4::class) 90 @TestableLooper.RunWithLooper 91 class MediaDataFilterImplTest : SysuiTestCase() { 92 val kosmos = testKosmos() 93 94 @Mock private lateinit var listener: MediaDataProcessor.Listener 95 @Mock private lateinit var userTracker: UserTracker 96 @Mock private lateinit var broadcastSender: BroadcastSender 97 @Mock private lateinit var mediaDataProcessor: MediaDataProcessor 98 @Mock private lateinit var lockscreenUserManager: NotificationLockscreenUserManager 99 @Mock private lateinit var executor: Executor 100 @Mock private lateinit var smartspaceData: SmartspaceMediaData 101 @Mock private lateinit var smartspaceMediaRecommendationItem: SmartspaceAction 102 @Mock private lateinit var logger: MediaUiEventLogger 103 @Mock private lateinit var mediaFlags: MediaFlags 104 @Mock private lateinit var cardAction: SmartspaceAction 105 106 private lateinit var mediaDataFilter: MediaDataFilterImpl 107 private lateinit var testScope: TestScope 108 private lateinit var dataMain: MediaData 109 private lateinit var dataGuest: MediaData 110 private lateinit var dataPrivateProfile: MediaData 111 private val clock = FakeSystemClock() 112 private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger 113 private val repository: MediaFilterRepository = <lambda>null114 with(kosmos) { 115 mediaSmartspaceLogger = mockMediaSmartspaceLogger 116 mediaFilterRepository 117 } 118 private val mediaLogger = kosmos.mockMediaLogger 119 120 @Before setupnull121 fun setup() { 122 MockitoAnnotations.initMocks(this) 123 MediaPlayerData.clear() 124 whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false) 125 testScope = TestScope() 126 mediaDataFilter = 127 MediaDataFilterImpl( 128 context, 129 userTracker, 130 broadcastSender, 131 lockscreenUserManager, 132 executor, 133 clock, 134 logger, 135 mediaFlags, 136 repository, 137 mediaLogger, 138 ) 139 mediaDataFilter.mediaDataProcessor = mediaDataProcessor 140 mediaDataFilter.addListener(listener) 141 142 // Start all tests as main user 143 setUser(USER_MAIN) 144 145 // Set up test media data 146 dataMain = 147 MediaTestUtils.emptyMediaData.copy( 148 userId = USER_MAIN, 149 packageName = PACKAGE, 150 instanceId = INSTANCE_ID, 151 appUid = APP_UID 152 ) 153 dataGuest = dataMain.copy(userId = USER_GUEST, instanceId = INSTANCE_ID_GUEST) 154 dataPrivateProfile = dataMain.copy(userId = PRIVATE_PROFILE, instanceId = INSTANCE_ID_GUEST) 155 156 whenever(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY) 157 whenever(smartspaceData.isActive).thenReturn(true) 158 whenever(smartspaceData.isValid()).thenReturn(true) 159 whenever(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE) 160 whenever(smartspaceData.recommendations) 161 .thenReturn(listOf(smartspaceMediaRecommendationItem)) 162 whenever(smartspaceMediaRecommendationItem.icon) 163 .thenReturn(Icon.createWithResource(context, R.drawable.ic_media_play)) 164 whenever(smartspaceData.headphoneConnectionTimeMillis) 165 .thenReturn(clock.currentTimeMillis() - 100) 166 whenever(smartspaceData.instanceId).thenReturn(SMARTSPACE_INSTANCE_ID) 167 whenever(smartspaceData.cardAction).thenReturn(cardAction) 168 } 169 setUsernull170 private fun setUser(id: Int) { 171 whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false) 172 whenever(lockscreenUserManager.isProfileAvailable(anyInt())).thenReturn(false) 173 whenever(lockscreenUserManager.isCurrentProfile(eq(id))).thenReturn(true) 174 whenever(lockscreenUserManager.isProfileAvailable(eq(id))).thenReturn(true) 175 whenever(lockscreenUserManager.isProfileAvailable(eq(PRIVATE_PROFILE))).thenReturn(true) 176 mediaDataFilter.handleUserSwitched() 177 } 178 setPrivateProfileUnavailablenull179 private fun setPrivateProfileUnavailable() { 180 whenever(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false) 181 whenever(lockscreenUserManager.isCurrentProfile(eq(USER_MAIN))).thenReturn(true) 182 whenever(lockscreenUserManager.isCurrentProfile(eq(PRIVATE_PROFILE))).thenReturn(true) 183 whenever(lockscreenUserManager.isProfileAvailable(eq(PRIVATE_PROFILE))).thenReturn(false) 184 mediaDataFilter.handleProfileChanged() 185 } 186 187 @Test onDataLoadedForCurrentUser_updatesLoadedStatesnull188 fun onDataLoadedForCurrentUser_updatesLoadedStates() = 189 testScope.runTest { 190 val currentMedia by collectLastValue(repository.currentMedia) 191 val mediaCommonModel = 192 MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(dataMain.instanceId)) 193 194 mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) 195 196 verify(listener) 197 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), eq(0), eq(false)) 198 verify(mediaLogger) 199 .logMediaLoaded(eq(dataMain.instanceId), eq(dataMain.active), anyString()) 200 assertThat(currentMedia).containsExactly(mediaCommonModel) 201 } 202 203 @Test onDataLoadedForGuest_doesNotUpdateLoadedStatesnull204 fun onDataLoadedForGuest_doesNotUpdateLoadedStates() = 205 testScope.runTest { 206 val currentMedia by collectLastValue(repository.currentMedia) 207 val mediaCommonModel = 208 MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(dataMain.instanceId)) 209 210 mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest) 211 212 verify(listener, never()) 213 .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean()) 214 verify(mediaLogger, never()).logMediaLoaded(any(), anyBoolean(), anyString()) 215 assertThat(currentMedia).doesNotContain(mediaCommonModel) 216 } 217 218 @Test onRemovedForCurrent_updatesLoadedStatesnull219 fun onRemovedForCurrent_updatesLoadedStates() = 220 testScope.runTest { 221 val currentMedia by collectLastValue(repository.currentMedia) 222 val mediaCommonModel = 223 MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(dataMain.instanceId)) 224 225 // GIVEN a media was removed for main user 226 mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) 227 228 verify(mediaLogger) 229 .logMediaLoaded(eq(dataMain.instanceId), eq(dataMain.active), anyString()) 230 assertThat(currentMedia).containsExactly(mediaCommonModel) 231 232 mediaDataFilter.onMediaDataRemoved(KEY, false) 233 234 verify(listener).onMediaDataRemoved(eq(KEY), eq(false)) 235 verify(mediaLogger).logMediaRemoved(eq(dataMain.instanceId), anyString()) 236 assertThat(currentMedia).doesNotContain(mediaCommonModel) 237 } 238 239 @Test onRemovedForGuest_doesNotUpdateLoadedStatesnull240 fun onRemovedForGuest_doesNotUpdateLoadedStates() = 241 testScope.runTest { 242 val currentMedia by collectLastValue(repository.currentMedia) 243 244 // GIVEN a media was removed for guest user 245 mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest) 246 mediaDataFilter.onMediaDataRemoved(KEY, false) 247 248 verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false)) 249 verify(mediaLogger, never()).logMediaRemoved(eq(dataGuest.instanceId), anyString()) 250 assertThat(currentMedia).isEmpty() 251 } 252 253 @Test onUserSwitched_removesOldUserControlsnull254 fun onUserSwitched_removesOldUserControls() = 255 testScope.runTest { 256 val currentMedia by collectLastValue(repository.currentMedia) 257 val mediaLoaded = MediaDataLoadingModel.Loaded(dataMain.instanceId) 258 259 // GIVEN that we have a media loaded for main user 260 mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) 261 262 verify(mediaLogger) 263 .logMediaLoaded(eq(dataMain.instanceId), eq(dataMain.active), anyString()) 264 assertThat(currentMedia).containsExactly(MediaCommonModel.MediaControl(mediaLoaded)) 265 266 // and we switch to guest user 267 setUser(USER_GUEST) 268 269 // THEN we should remove the main user's media 270 verify(listener).onMediaDataRemoved(eq(KEY), eq(false)) 271 verify(mediaLogger).logMediaRemoved(eq(dataMain.instanceId), anyString()) 272 assertThat(currentMedia).isEmpty() 273 } 274 275 @Test onUserSwitched_addsNewUserControlsnull276 fun onUserSwitched_addsNewUserControls() = 277 testScope.runTest { 278 val currentMedia by collectLastValue(repository.currentMedia) 279 val guestLoadedStatesModel = MediaDataLoadingModel.Loaded(dataGuest.instanceId) 280 val mainLoadedStatesModel = MediaDataLoadingModel.Loaded(dataMain.instanceId) 281 282 // GIVEN that we had some media for both users 283 mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) 284 mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataGuest) 285 286 // and we switch to guest user 287 setUser(USER_GUEST) 288 289 // THEN we should add back the guest user media 290 verify(listener) 291 .onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true), eq(0), eq(false)) 292 verify(mediaLogger) 293 .logMediaLoaded(eq(dataGuest.instanceId), eq(dataGuest.active), anyString()) 294 295 reset(mediaLogger) 296 297 // but not the main user's 298 verify(listener, never()) 299 .onMediaDataLoaded( 300 eq(KEY), 301 any(), 302 eq(dataMain), 303 anyBoolean(), 304 anyInt(), 305 anyBoolean() 306 ) 307 verify(mediaLogger, never()) 308 .logMediaLoaded(eq(dataMain.instanceId), anyBoolean(), anyString()) 309 assertThat(currentMedia) 310 .containsExactly(MediaCommonModel.MediaControl(guestLoadedStatesModel)) 311 assertThat(currentMedia) 312 .doesNotContain(MediaCommonModel.MediaControl(mainLoadedStatesModel)) 313 } 314 315 @Test onProfileChanged_profileUnavailable_updateStatesnull316 fun onProfileChanged_profileUnavailable_updateStates() = 317 testScope.runTest { 318 val currentMedia by collectLastValue(repository.currentMedia) 319 320 // GIVEN that we had some media for both profiles 321 mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) 322 mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataPrivateProfile) 323 324 // and we change profile status 325 setPrivateProfileUnavailable() 326 327 val mediaLoadedStatesModel = MediaDataLoadingModel.Loaded(dataMain.instanceId) 328 // THEN we should remove the private profile media 329 verify(listener).onMediaDataRemoved(eq(KEY_ALT), eq(false)) 330 verify(mediaLogger).logMediaRemoved(eq(dataGuest.instanceId), anyString()) 331 assertThat(currentMedia) 332 .containsExactly(MediaCommonModel.MediaControl(mediaLoadedStatesModel)) 333 } 334 335 @Test hasAnyMedia_mediaSet_returnsTruenull336 fun hasAnyMedia_mediaSet_returnsTrue() = 337 testScope.runTest { 338 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 339 mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain) 340 341 assertThat(hasAnyMedia(selectedUserEntries)).isTrue() 342 } 343 344 @Test hasAnyMedia_recommendationSet_returnsFalsenull345 fun hasAnyMedia_recommendationSet_returnsFalse() = 346 testScope.runTest { 347 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 348 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 349 350 assertThat(hasAnyMedia(selectedUserEntries)).isFalse() 351 } 352 353 @Test hasAnyMediaOrRecommendation_mediaSet_returnsTruenull354 fun hasAnyMediaOrRecommendation_mediaSet_returnsTrue() = 355 testScope.runTest { 356 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 357 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 358 mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain) 359 360 assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData)) 361 .isTrue() 362 } 363 364 @Test hasAnyMediaOrRecommendation_recommendationSet_returnsTruenull365 fun hasAnyMediaOrRecommendation_recommendationSet_returnsTrue() = 366 testScope.runTest { 367 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 368 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 369 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 370 371 assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData)) 372 .isTrue() 373 } 374 375 @Test hasActiveMedia_inactiveMediaSet_returnsFalsenull376 fun hasActiveMedia_inactiveMediaSet_returnsFalse() = 377 testScope.runTest { 378 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 379 380 val data = dataMain.copy(active = false) 381 mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data) 382 383 assertThat(hasActiveMedia(selectedUserEntries)).isFalse() 384 } 385 386 @Test hasActiveMedia_activeMediaSet_returnsTruenull387 fun hasActiveMedia_activeMediaSet_returnsTrue() = 388 testScope.runTest { 389 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 390 val data = dataMain.copy(active = true) 391 mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data) 392 393 assertThat(hasActiveMedia(selectedUserEntries)).isTrue() 394 } 395 396 @Test hasActiveMediaOrRecommendation_inactiveMediaSet_returnsFalsenull397 fun hasActiveMediaOrRecommendation_inactiveMediaSet_returnsFalse() = 398 testScope.runTest { 399 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 400 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 401 val reactivatedKey by collectLastValue(repository.reactivatedId) 402 val data = dataMain.copy(active = false) 403 mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data) 404 405 assertThat( 406 hasActiveMediaOrRecommendation( 407 selectedUserEntries, 408 smartspaceMediaData, 409 reactivatedKey 410 ) 411 ) 412 .isFalse() 413 } 414 415 @Test hasActiveMediaOrRecommendation_activeMediaSet_returnsTruenull416 fun hasActiveMediaOrRecommendation_activeMediaSet_returnsTrue() = 417 testScope.runTest { 418 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 419 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 420 val reactivatedKey by collectLastValue(repository.reactivatedId) 421 val data = dataMain.copy(active = true) 422 mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data) 423 424 assertThat( 425 hasActiveMediaOrRecommendation( 426 selectedUserEntries, 427 smartspaceMediaData, 428 reactivatedKey 429 ) 430 ) 431 .isTrue() 432 } 433 434 @Test hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalsenull435 fun hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalse() = 436 testScope.runTest { 437 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 438 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 439 val reactivatedKey by collectLastValue(repository.reactivatedId) 440 whenever(smartspaceData.isActive).thenReturn(false) 441 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 442 443 assertThat( 444 hasActiveMediaOrRecommendation( 445 selectedUserEntries, 446 smartspaceMediaData, 447 reactivatedKey 448 ) 449 ) 450 .isFalse() 451 } 452 453 @Test hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalsenull454 fun hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalse() = 455 testScope.runTest { 456 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 457 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 458 val reactivatedKey by collectLastValue(repository.reactivatedId) 459 whenever(smartspaceData.isValid()).thenReturn(false) 460 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 461 462 assertThat( 463 hasActiveMediaOrRecommendation( 464 selectedUserEntries, 465 smartspaceMediaData, 466 reactivatedKey 467 ) 468 ) 469 .isFalse() 470 } 471 472 @Test hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTruenull473 fun hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTrue() = 474 testScope.runTest { 475 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 476 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 477 val reactivatedKey by collectLastValue(repository.reactivatedId) 478 whenever(smartspaceData.isActive).thenReturn(true) 479 whenever(smartspaceData.isValid()).thenReturn(true) 480 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 481 482 assertThat( 483 hasActiveMediaOrRecommendation( 484 selectedUserEntries, 485 smartspaceMediaData, 486 reactivatedKey 487 ) 488 ) 489 .isTrue() 490 } 491 492 @Test hasAnyMediaOrRecommendation_onlyCurrentUsernull493 fun hasAnyMediaOrRecommendation_onlyCurrentUser() = 494 testScope.runTest { 495 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 496 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 497 assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData)) 498 .isFalse() 499 500 mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataGuest) 501 assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData)) 502 .isFalse() 503 assertThat(hasAnyMedia(selectedUserEntries)).isFalse() 504 } 505 506 @Test hasActiveMediaOrRecommendation_onlyCurrentUsernull507 fun hasActiveMediaOrRecommendation_onlyCurrentUser() = 508 testScope.runTest { 509 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 510 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 511 val reactivatedKey by collectLastValue(repository.reactivatedId) 512 assertThat( 513 hasActiveMediaOrRecommendation( 514 selectedUserEntries, 515 smartspaceMediaData, 516 reactivatedKey 517 ) 518 ) 519 .isFalse() 520 val data = dataGuest.copy(active = true) 521 522 mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data) 523 assertThat( 524 hasActiveMediaOrRecommendation( 525 selectedUserEntries, 526 smartspaceMediaData, 527 reactivatedKey 528 ) 529 ) 530 .isFalse() 531 assertThat(hasAnyMedia(selectedUserEntries)).isFalse() 532 } 533 534 @Test onNotificationRemoved_doesNotHaveMedianull535 fun onNotificationRemoved_doesNotHaveMedia() = 536 testScope.runTest { 537 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 538 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 539 540 mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain) 541 mediaDataFilter.onMediaDataRemoved(KEY, false) 542 assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData)) 543 .isFalse() 544 assertThat(hasAnyMedia(selectedUserEntries)).isFalse() 545 } 546 547 @Test onSwipeToDismiss_setsTimedOutnull548 fun onSwipeToDismiss_setsTimedOut() { 549 mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) 550 mediaDataFilter.onSwipeToDismiss(1) 551 552 verify(smartspaceLogger, never()) 553 .logSmartspaceCardUIEvent( 554 eq(MediaSmartspaceLogger.SMARTSPACE_CARD_DISMISS_EVENT), 555 anyInt(), 556 anyInt(), 557 anyInt(), 558 anyInt(), 559 anyBoolean(), 560 anyBoolean(), 561 anyInt(), 562 anyInt(), 563 anyInt(), 564 eq(true) 565 ) 566 verify(mediaDataProcessor).setInactive(eq(KEY), eq(true), eq(true)) 567 } 568 569 @Test onSmartspaceMediaDataLoaded_noMedia_activeValidRec_prioritizesSmartspacenull570 fun onSmartspaceMediaDataLoaded_noMedia_activeValidRec_prioritizesSmartspace() = 571 testScope.runTest { 572 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 573 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 574 val reactivatedKey by collectLastValue(repository.reactivatedId) 575 val currentMedia by collectLastValue(repository.currentMedia) 576 val recommendationsLoadingModel = 577 SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY, isPrioritized = true) 578 579 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 580 581 assertThat(currentMedia) 582 .containsExactly(MediaCommonModel.MediaRecommendations(recommendationsLoadingModel)) 583 assertThat( 584 hasActiveMediaOrRecommendation( 585 selectedUserEntries, 586 smartspaceMediaData, 587 reactivatedKey 588 ) 589 ) 590 .isTrue() 591 assertThat(hasActiveMedia(selectedUserEntries)).isFalse() 592 verify(listener) 593 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true)) 594 verify(mediaLogger).logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(true), anyString()) 595 verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID) 596 verify(logger, never()).logRecommendationActivated(any(), any(), any()) 597 } 598 599 @Test onSmartspaceMediaDataLoaded_noMedia_inactiveRec_showsNothingnull600 fun onSmartspaceMediaDataLoaded_noMedia_inactiveRec_showsNothing() = 601 testScope.runTest { 602 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 603 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 604 val reactivatedKey by collectLastValue(repository.reactivatedId) 605 val currentMedia by collectLastValue(repository.currentMedia) 606 607 whenever(smartspaceData.isActive).thenReturn(false) 608 609 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 610 611 assertThat(currentMedia).isEmpty() 612 assertThat( 613 hasActiveMediaOrRecommendation( 614 selectedUserEntries, 615 smartspaceMediaData, 616 reactivatedKey 617 ) 618 ) 619 .isFalse() 620 assertThat(hasActiveMedia(selectedUserEntries)).isFalse() 621 verify(listener, never()) 622 .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean()) 623 verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) 624 verify(mediaLogger, never()).logMediaLoaded(any(), anyBoolean(), anyString()) 625 verify(mediaLogger, never()).logRecommendationLoaded(any(), anyBoolean(), anyString()) 626 verify(logger, never()).logRecommendationAdded(any(), any()) 627 verify(logger, never()).logRecommendationActivated(any(), any(), any()) 628 } 629 630 @Test onSmartspaceMediaDataLoaded_noRecentMedia_activeValidRec_prioritizesSmartspacenull631 fun onSmartspaceMediaDataLoaded_noRecentMedia_activeValidRec_prioritizesSmartspace() = 632 testScope.runTest { 633 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 634 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 635 val reactivatedKey by collectLastValue(repository.reactivatedId) 636 val currentMedia by collectLastValue(repository.currentMedia) 637 val recsCommonModel = 638 MediaCommonModel.MediaRecommendations( 639 SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY, isPrioritized = true) 640 ) 641 val controlCommonModel = 642 MediaCommonModel.MediaControl( 643 MediaDataLoadingModel.Loaded(dataMain.instanceId), 644 true 645 ) 646 val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) 647 mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld) 648 clock.advanceTime(MediaDataFilterImpl.SMARTSPACE_MAX_AGE + 100) 649 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 650 651 assertThat(currentMedia).containsExactly(recsCommonModel, controlCommonModel) 652 assertThat( 653 hasActiveMediaOrRecommendation( 654 selectedUserEntries, 655 smartspaceMediaData, 656 reactivatedKey 657 ) 658 ) 659 .isTrue() 660 assertThat(hasActiveMedia(selectedUserEntries)).isFalse() 661 verify(listener) 662 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true)) 663 verify(mediaLogger).logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(true), anyString()) 664 verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID) 665 verify(logger, never()).logRecommendationActivated(any(), any(), any()) 666 } 667 668 @Test onSmartspaceMediaDataLoaded_noRecentMedia_inactiveRec_showsNothingnull669 fun onSmartspaceMediaDataLoaded_noRecentMedia_inactiveRec_showsNothing() = 670 testScope.runTest { 671 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 672 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 673 val reactivatedKey by collectLastValue(repository.reactivatedId) 674 val currentMedia by collectLastValue(repository.currentMedia) 675 whenever(smartspaceData.isActive).thenReturn(false) 676 677 val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) 678 mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld) 679 clock.advanceTime(MediaDataFilterImpl.SMARTSPACE_MAX_AGE + 100) 680 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 681 682 assertThat(currentMedia) 683 .doesNotContain( 684 MediaCommonModel.MediaRecommendations( 685 SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY) 686 ) 687 ) 688 assertThat( 689 hasActiveMediaOrRecommendation( 690 selectedUserEntries, 691 smartspaceMediaData, 692 reactivatedKey 693 ) 694 ) 695 .isFalse() 696 assertThat(hasActiveMedia(selectedUserEntries)).isFalse() 697 verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) 698 verify(mediaLogger, never()).logRecommendationLoaded(any(), anyBoolean(), anyString()) 699 verify(logger, never()).logRecommendationAdded(any(), any()) 700 verify(logger, never()).logRecommendationActivated(any(), any(), any()) 701 } 702 703 @Test onSmartspaceMediaDataLoaded_hasRecentMedia_inactiveRec_showsNothingnull704 fun onSmartspaceMediaDataLoaded_hasRecentMedia_inactiveRec_showsNothing() = 705 testScope.runTest { 706 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 707 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 708 val reactivatedKey by collectLastValue(repository.reactivatedId) 709 val currentMedia by collectLastValue(repository.currentMedia) 710 711 whenever(smartspaceData.isActive).thenReturn(false) 712 713 // WHEN we have media that was recently played, but not currently active 714 val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) 715 val controlCommonModel = 716 MediaCommonModel.MediaControl( 717 MediaDataLoadingModel.Loaded(dataMain.instanceId), 718 true 719 ) 720 mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) 721 repository.setOrderedMedia() 722 723 assertThat(currentMedia).containsExactly(controlCommonModel) 724 verify(listener) 725 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) 726 verify(mediaLogger) 727 .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString()) 728 729 reset(mediaLogger) 730 731 // AND we get a smartspace signal 732 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 733 734 // THEN we should treat the media as not active instead 735 assertThat(currentMedia).containsExactly(controlCommonModel) 736 assertThat( 737 hasActiveMediaOrRecommendation( 738 selectedUserEntries, 739 smartspaceMediaData, 740 reactivatedKey 741 ) 742 ) 743 .isFalse() 744 assertThat(hasActiveMedia(selectedUserEntries)).isFalse() 745 verify(listener, never()) 746 .onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(), anyInt(), anyBoolean()) 747 verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) 748 verify(mediaLogger, never()) 749 .logMediaLoaded(eq(dataCurrent.instanceId), anyBoolean(), anyString()) 750 verify(mediaLogger, never()).logRecommendationLoaded(any(), anyBoolean(), anyString()) 751 verify(logger, never()).logRecommendationAdded(any(), any()) 752 verify(logger, never()).logRecommendationActivated(any(), any(), any()) 753 } 754 755 @Test onSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedianull756 fun onSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedia() = 757 testScope.runTest { 758 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 759 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 760 val reactivatedKey by collectLastValue(repository.reactivatedId) 761 val currentMedia by collectLastValue(repository.currentMedia) 762 whenever(smartspaceData.isValid()).thenReturn(false) 763 764 // WHEN we have media that was recently played, but not currently active 765 val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) 766 val mediaLoadingModel = MediaDataLoadingModel.Loaded(dataMain.instanceId) 767 var controlCommonModel = MediaCommonModel.MediaControl(mediaLoadingModel, true) 768 mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) 769 repository.setOrderedMedia() 770 assertThat(currentMedia).containsExactly(controlCommonModel) 771 verify(listener) 772 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) 773 verify(mediaLogger) 774 .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString()) 775 776 // AND we get a smartspace signal 777 runCurrent() 778 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 779 780 // THEN we should treat the media as active instead 781 val dataCurrentAndActive = 782 dataMain.copy(active = true, lastActive = clock.elapsedRealtime()) 783 controlCommonModel = 784 controlCommonModel.copy( 785 mediaLoadingModel.copy( 786 receivedSmartspaceCardLatency = 100, 787 isSsReactivated = true 788 ) 789 ) 790 assertThat(currentMedia).containsExactly(controlCommonModel) 791 assertThat( 792 hasActiveMediaOrRecommendation( 793 selectedUserEntries, 794 smartspaceMediaData, 795 reactivatedKey 796 ) 797 ) 798 .isTrue() 799 verify(listener) 800 .onMediaDataLoaded( 801 eq(KEY), 802 eq(KEY), 803 eq(dataCurrentAndActive), 804 eq(true), 805 eq(100), 806 eq(true) 807 ) 808 verify(mediaLogger) 809 .logMediaLoaded( 810 eq(dataCurrentAndActive.instanceId), 811 eq(dataCurrentAndActive.active), 812 anyString() 813 ) 814 // Smartspace update shouldn't be propagated for the empty rec list. 815 verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) 816 verify(mediaLogger, never()).logRecommendationLoaded(any(), anyBoolean(), anyString()) 817 verify(logger, never()).logRecommendationAdded(any(), any()) 818 verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID)) 819 } 820 821 @Test onSmartspaceMediaDataLoaded_hasRecentMedia_activeValidRec_usesBothnull822 fun onSmartspaceMediaDataLoaded_hasRecentMedia_activeValidRec_usesBoth() = 823 testScope.runTest { 824 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 825 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 826 val reactivatedKey by collectLastValue(repository.reactivatedId) 827 val currentMedia by collectLastValue(repository.currentMedia) 828 // WHEN we have media that was recently played, but not currently active 829 val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) 830 val mediaLoadingModel = MediaDataLoadingModel.Loaded(dataMain.instanceId) 831 var controlCommonModel = MediaCommonModel.MediaControl(mediaLoadingModel, true) 832 val recsCommonModel = 833 MediaCommonModel.MediaRecommendations( 834 SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY) 835 ) 836 837 mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) 838 repository.setOrderedMedia() 839 840 assertThat(currentMedia).containsExactly(controlCommonModel) 841 verify(listener) 842 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) 843 verify(mediaLogger) 844 .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString()) 845 846 // AND we get a smartspace signal 847 runCurrent() 848 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 849 850 // THEN we should treat the media as active instead 851 val dataCurrentAndActive = 852 dataMain.copy(active = true, lastActive = clock.elapsedRealtime()) 853 verify(listener) 854 .onMediaDataLoaded( 855 eq(KEY), 856 eq(KEY), 857 eq(dataCurrentAndActive), 858 eq(true), 859 eq(100), 860 eq(true) 861 ) 862 verify(mediaLogger) 863 .logMediaLoaded( 864 eq(dataCurrentAndActive.instanceId), 865 eq(dataCurrentAndActive.active), 866 anyString() 867 ) 868 assertThat( 869 hasActiveMediaOrRecommendation( 870 selectedUserEntries, 871 smartspaceMediaData, 872 reactivatedKey 873 ) 874 ) 875 .isTrue() 876 // Smartspace update should also be propagated but not prioritized. 877 controlCommonModel = 878 controlCommonModel.copy( 879 mediaLoadingModel.copy( 880 receivedSmartspaceCardLatency = 100, 881 isSsReactivated = true 882 ) 883 ) 884 assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel) 885 verify(listener) 886 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) 887 verify(mediaLogger).logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(true), anyString()) 888 verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID) 889 verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID)) 890 } 891 892 @Test onSmartspaceMediaDataRemoved_usedSmartspace_clearsSmartspacenull893 fun onSmartspaceMediaDataRemoved_usedSmartspace_clearsSmartspace() = 894 testScope.runTest { 895 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 896 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 897 val reactivatedKey by collectLastValue(repository.reactivatedId) 898 val currentMedia by collectLastValue(repository.currentMedia) 899 900 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 901 mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) 902 903 verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) 904 verify(mediaLogger).logRecommendationRemoved(eq(SMARTSPACE_KEY), eq(true), anyString()) 905 assertThat(currentMedia).isEmpty() 906 assertThat( 907 hasActiveMediaOrRecommendation( 908 selectedUserEntries, 909 smartspaceMediaData, 910 reactivatedKey 911 ) 912 ) 913 .isFalse() 914 assertThat(hasActiveMedia(selectedUserEntries)).isFalse() 915 } 916 917 @Test onSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBothnull918 fun onSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() = 919 testScope.runTest { 920 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 921 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 922 val reactivatedKey by collectLastValue(repository.reactivatedId) 923 val currentMedia by collectLastValue(repository.currentMedia) 924 val controlCommonModel = 925 MediaCommonModel.MediaControl( 926 MediaDataLoadingModel.Loaded(dataMain.instanceId), 927 true 928 ) 929 val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) 930 mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) 931 repository.setOrderedMedia() 932 933 assertThat(currentMedia).containsExactly(controlCommonModel) 934 verify(listener) 935 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) 936 verify(mediaLogger) 937 .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString()) 938 939 runCurrent() 940 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 941 942 val dataCurrentAndActive = 943 dataMain.copy(active = true, lastActive = clock.elapsedRealtime()) 944 verify(listener) 945 .onMediaDataLoaded( 946 eq(KEY), 947 eq(KEY), 948 eq(dataCurrentAndActive), 949 eq(true), 950 eq(100), 951 eq(true) 952 ) 953 verify(mediaLogger) 954 .logMediaLoaded( 955 eq(dataCurrentAndActive.instanceId), 956 eq(dataCurrentAndActive.active), 957 anyString() 958 ) 959 960 mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) 961 962 verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) 963 verify(mediaLogger).logRecommendationRemoved(eq(SMARTSPACE_KEY), eq(true), anyString()) 964 assertThat(currentMedia).containsExactly(controlCommonModel) 965 assertThat( 966 hasActiveMediaOrRecommendation( 967 selectedUserEntries, 968 smartspaceMediaData, 969 reactivatedKey 970 ) 971 ) 972 .isFalse() 973 assertThat(hasActiveMedia(selectedUserEntries)).isFalse() 974 } 975 976 @Test onSmartspaceLoaded_persistentEnabled_isInactivenull977 fun onSmartspaceLoaded_persistentEnabled_isInactive() = 978 testScope.runTest { 979 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 980 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 981 val reactivatedKey by collectLastValue(repository.reactivatedId) 982 val currentMedia by collectLastValue(repository.currentMedia) 983 val recsCommonModel = 984 MediaCommonModel.MediaRecommendations( 985 SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY) 986 ) 987 whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true) 988 whenever(smartspaceData.isActive).thenReturn(false) 989 990 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 991 992 verify(listener) 993 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) 994 verify(mediaLogger).logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(false), anyString()) 995 assertThat(currentMedia).containsExactly(recsCommonModel) 996 assertThat( 997 hasActiveMediaOrRecommendation( 998 selectedUserEntries, 999 smartspaceMediaData, 1000 reactivatedKey 1001 ) 1002 ) 1003 .isFalse() 1004 assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData)) 1005 .isTrue() 1006 } 1007 1008 @Test onSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactivenull1009 fun onSmartspaceLoaded_persistentEnabled_inactive_hasRecentMedia_staysInactive() = 1010 testScope.runTest { 1011 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 1012 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 1013 val reactivatedKey by collectLastValue(repository.reactivatedId) 1014 val currentMedia by collectLastValue(repository.currentMedia) 1015 val recsCommonModel = 1016 MediaCommonModel.MediaRecommendations( 1017 SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY) 1018 ) 1019 val controlCommonModel = 1020 MediaCommonModel.MediaControl( 1021 MediaDataLoadingModel.Loaded(dataMain.instanceId), 1022 true 1023 ) 1024 1025 whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true) 1026 whenever(smartspaceData.isActive).thenReturn(false) 1027 1028 // If there is media that was recently played but inactive 1029 val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) 1030 mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) 1031 repository.setOrderedMedia() 1032 1033 verify(listener) 1034 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) 1035 verify(mediaLogger) 1036 .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString()) 1037 assertThat(currentMedia).containsExactly(controlCommonModel) 1038 1039 reset(mediaLogger) 1040 1041 // And an inactive recommendation is loaded 1042 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 1043 1044 // Smartspace is loaded but the media stays inactive 1045 verify(listener) 1046 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) 1047 verify(mediaLogger).logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(false), anyString()) 1048 verify(listener, never()) 1049 .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean()) 1050 verify(mediaLogger, never()).logMediaLoaded(any(), anyBoolean(), anyString()) 1051 assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel) 1052 assertThat( 1053 hasActiveMediaOrRecommendation( 1054 selectedUserEntries, 1055 smartspaceMediaData, 1056 reactivatedKey 1057 ) 1058 ) 1059 .isFalse() 1060 assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData)) 1061 .isTrue() 1062 } 1063 1064 @Test onSwipeToDismiss_persistentEnabled_recommendationSetInactivenull1065 fun onSwipeToDismiss_persistentEnabled_recommendationSetInactive() { 1066 whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true) 1067 1068 val data = 1069 EMPTY_SMARTSPACE_MEDIA_DATA.copy( 1070 targetId = SMARTSPACE_KEY, 1071 isActive = true, 1072 packageName = SMARTSPACE_PACKAGE, 1073 recommendations = 1074 listOf( 1075 smartspaceMediaRecommendationItem, 1076 smartspaceMediaRecommendationItem, 1077 smartspaceMediaRecommendationItem 1078 ), 1079 ) 1080 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data) 1081 1082 mediaDataFilter.onSwipeToDismiss(1) 1083 1084 verify(smartspaceLogger) 1085 .logSmartspaceCardUIEvent( 1086 MediaSmartspaceLogger.SMARTSPACE_CARD_DISMISS_EVENT, 1087 SmallHash.hash(data.targetId), 1088 Process.INVALID_UID, 1089 surface = 1, 1090 cardinality = 1, 1091 isRecommendationCard = true, 1092 isSwipeToDismiss = true 1093 ) 1094 verify(mediaDataProcessor).setRecommendationInactive(eq(SMARTSPACE_KEY)) 1095 verify(mediaDataProcessor, never()) 1096 .dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong()) 1097 } 1098 1099 @Test smartspaceLoaded_shouldTriggerResume_doesTriggernull1100 fun smartspaceLoaded_shouldTriggerResume_doesTrigger() = 1101 testScope.runTest { 1102 val selectedUserEntries by collectLastValue(repository.selectedUserEntries) 1103 val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData) 1104 val reactivatedKey by collectLastValue(repository.reactivatedId) 1105 val currentMedia by collectLastValue(repository.currentMedia) 1106 val recsCommonModel = 1107 MediaCommonModel.MediaRecommendations( 1108 SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY) 1109 ) 1110 val mediaLoadingModel = MediaDataLoadingModel.Loaded(dataMain.instanceId) 1111 var controlCommonModel = MediaCommonModel.MediaControl(mediaLoadingModel, true) 1112 // WHEN we have media that was recently played, but not currently active 1113 val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) 1114 mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) 1115 repository.setOrderedMedia() 1116 1117 verify(listener) 1118 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) 1119 verify(mediaLogger) 1120 .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString()) 1121 assertThat(currentMedia).containsExactly(controlCommonModel) 1122 1123 // AND we get a smartspace signal with extra to trigger resume 1124 runCurrent() 1125 val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, true) } 1126 whenever(cardAction.extras).thenReturn(extras) 1127 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 1128 1129 // THEN we should treat the media as active instead 1130 val dataCurrentAndActive = 1131 dataMain.copy(active = true, lastActive = clock.elapsedRealtime()) 1132 controlCommonModel = 1133 controlCommonModel.copy( 1134 mediaLoadingModel.copy( 1135 receivedSmartspaceCardLatency = 100, 1136 isSsReactivated = true 1137 ) 1138 ) 1139 verify(listener) 1140 .onMediaDataLoaded( 1141 eq(KEY), 1142 eq(KEY), 1143 eq(dataCurrentAndActive), 1144 eq(true), 1145 eq(100), 1146 eq(true) 1147 ) 1148 verify(mediaLogger) 1149 .logMediaLoaded( 1150 eq(dataCurrentAndActive.instanceId), 1151 eq(dataCurrentAndActive.active), 1152 anyString() 1153 ) 1154 assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel) 1155 assertThat( 1156 hasActiveMediaOrRecommendation( 1157 selectedUserEntries, 1158 smartspaceMediaData, 1159 reactivatedKey 1160 ) 1161 ) 1162 .isTrue() 1163 // And update the smartspace data state, but not prioritized 1164 verify(listener) 1165 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) 1166 verify(mediaLogger).logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(true), anyString()) 1167 } 1168 1169 @Test smartspaceLoaded_notShouldTriggerResume_doesNotTriggernull1170 fun smartspaceLoaded_notShouldTriggerResume_doesNotTrigger() = 1171 testScope.runTest { 1172 val currentMedia by collectLastValue(repository.currentMedia) 1173 val recsCommonModel = 1174 MediaCommonModel.MediaRecommendations( 1175 SmartspaceMediaLoadingModel.Loaded(SMARTSPACE_KEY) 1176 ) 1177 val controlCommonModel = 1178 MediaCommonModel.MediaControl( 1179 MediaDataLoadingModel.Loaded(dataMain.instanceId), 1180 true 1181 ) 1182 1183 // WHEN we have media that was recently played, but not currently active 1184 val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) 1185 mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) 1186 repository.setOrderedMedia() 1187 1188 verify(listener) 1189 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) 1190 verify(mediaLogger) 1191 .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString()) 1192 assertThat(currentMedia).containsExactly(controlCommonModel) 1193 1194 reset(mediaLogger) 1195 1196 // AND we get a smartspace signal with extra to not trigger resume 1197 val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, false) } 1198 whenever(cardAction.extras).thenReturn(extras) 1199 mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) 1200 1201 // THEN listeners are not updated to show media 1202 verify(listener, never()) 1203 .onMediaDataLoaded(eq(KEY), eq(KEY), any(), eq(true), eq(100), eq(true)) 1204 verify(mediaLogger, never()) 1205 .logMediaLoaded(eq(dataCurrent.instanceId), anyBoolean(), anyString()) 1206 // But the smartspace update is still propagated 1207 verify(listener) 1208 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) 1209 verify(mediaLogger).logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(true), anyString()) 1210 assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel) 1211 } 1212 hasActiveMediaOrRecommendationnull1213 private fun hasActiveMediaOrRecommendation( 1214 entries: Map<InstanceId, MediaData>?, 1215 smartspaceMediaData: SmartspaceMediaData?, 1216 reactivatedId: InstanceId? 1217 ): Boolean { 1218 if (entries == null || smartspaceMediaData == null) { 1219 return false 1220 } 1221 return entries.any { it.value.active } || 1222 (smartspaceMediaData.isActive && 1223 (smartspaceMediaData.isValid() || reactivatedId != null)) 1224 } 1225 hasActiveMedianull1226 private fun hasActiveMedia(entries: Map<InstanceId, MediaData>?): Boolean { 1227 return entries?.any { it.value.active } ?: false 1228 } 1229 hasAnyMediaOrRecommendationnull1230 private fun hasAnyMediaOrRecommendation( 1231 entries: Map<InstanceId, MediaData>?, 1232 smartspaceMediaData: SmartspaceMediaData? 1233 ): Boolean { 1234 if (entries == null || smartspaceMediaData == null) { 1235 return false 1236 } 1237 return entries.isNotEmpty() || 1238 (if (mediaFlags.isPersistentSsCardEnabled()) { 1239 smartspaceMediaData.isValid() 1240 } else { 1241 smartspaceMediaData.isActive && smartspaceMediaData.isValid() 1242 }) 1243 } 1244 hasAnyMedianull1245 private fun hasAnyMedia(entries: Map<InstanceId, MediaData>?): Boolean { 1246 return entries?.isNotEmpty() ?: false 1247 } 1248 } 1249