blob: 6a532d74967f1b3576bd6dee7d80bb3a57df2ae4 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.media
import android.app.smartspace.SmartspaceAction
import androidx.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.util.concurrent.Executor
private const val KEY = "TEST_KEY"
private const val KEY_ALT = "TEST_KEY_2"
private const val USER_MAIN = 0
private const val USER_GUEST = 10
private const val PACKAGE = "PKG"
private val INSTANCE_ID = InstanceId.fakeInstanceId(123)!!
private const val APP_UID = 99
private const val SMARTSPACE_KEY = "SMARTSPACE_KEY"
private const val SMARTSPACE_PACKAGE = "SMARTSPACE_PKG"
private val SMARTSPACE_INSTANCE_ID = InstanceId.fakeInstanceId(456)!!
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class MediaDataFilterTest : SysuiTestCase() {
@Mock
private lateinit var listener: MediaDataManager.Listener
@Mock
private lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock
private lateinit var broadcastSender: BroadcastSender
@Mock
private lateinit var mediaDataManager: MediaDataManager
@Mock
private lateinit var lockscreenUserManager: NotificationLockscreenUserManager
@Mock
private lateinit var executor: Executor
@Mock
private lateinit var smartspaceData: SmartspaceMediaData
@Mock
private lateinit var smartspaceMediaRecommendationItem: SmartspaceAction
@Mock
private lateinit var logger: MediaUiEventLogger
private lateinit var mediaDataFilter: MediaDataFilter
private lateinit var dataMain: MediaData
private lateinit var dataGuest: MediaData
private val clock = FakeSystemClock()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
MediaPlayerData.clear()
mediaDataFilter = MediaDataFilter(
context,
broadcastDispatcher,
broadcastSender,
lockscreenUserManager,
executor,
clock,
logger)
mediaDataFilter.mediaDataManager = mediaDataManager
mediaDataFilter.addListener(listener)
// Start all tests as main user
setUser(USER_MAIN)
// Set up test media data
dataMain = MediaTestUtils.emptyMediaData.copy(
userId = USER_MAIN,
packageName = PACKAGE,
instanceId = INSTANCE_ID,
appUid = APP_UID)
dataGuest = dataMain.copy(userId = USER_GUEST)
`when`(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
`when`(smartspaceData.isActive).thenReturn(true)
`when`(smartspaceData.isValid()).thenReturn(true)
`when`(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE)
`when`(smartspaceData.recommendations).thenReturn(listOf(smartspaceMediaRecommendationItem))
`when`(smartspaceData.headphoneConnectionTimeMillis).thenReturn(
clock.currentTimeMillis() - 100)
`when`(smartspaceData.instanceId).thenReturn(SMARTSPACE_INSTANCE_ID)
}
private fun setUser(id: Int) {
`when`(lockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false)
`when`(lockscreenUserManager.isCurrentProfile(eq(id))).thenReturn(true)
mediaDataFilter.handleUserSwitched(id)
}
@Test
fun testOnDataLoadedForCurrentUser_callsListener() {
// GIVEN a media for main user
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
// THEN we should tell the listener
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true),
eq(0), eq(false))
}
@Test
fun testOnDataLoadedForGuest_doesNotCallListener() {
// GIVEN a media for guest user
mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
// THEN we should NOT tell the listener
verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(),
anyInt(), anyBoolean())
}
@Test
fun testOnRemovedForCurrent_callsListener() {
// GIVEN a media was removed for main user
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
mediaDataFilter.onMediaDataRemoved(KEY)
// THEN we should tell the listener
verify(listener).onMediaDataRemoved(eq(KEY))
}
@Test
fun testOnRemovedForGuest_doesNotCallListener() {
// GIVEN a media was removed for guest user
mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
mediaDataFilter.onMediaDataRemoved(KEY)
// THEN we should NOT tell the listener
verify(listener, never()).onMediaDataRemoved(eq(KEY))
}
@Test
fun testOnUserSwitched_removesOldUserControls() {
// GIVEN that we have a media loaded for main user
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
// and we switch to guest user
setUser(USER_GUEST)
// THEN we should remove the main user's media
verify(listener).onMediaDataRemoved(eq(KEY))
}
@Test
fun testOnUserSwitched_addsNewUserControls() {
// GIVEN that we had some media for both users
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
mediaDataFilter.onMediaDataLoaded(KEY_ALT, null, dataGuest)
reset(listener)
// and we switch to guest user
setUser(USER_GUEST)
// THEN we should add back the guest user media
verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true),
eq(0), eq(false))
// but not the main user's
verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean(),
anyInt(), anyBoolean())
}
@Test
fun hasAnyMedia_noMediaSet_returnsFalse() {
assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
}
@Test
fun hasAnyMedia_mediaSet_returnsTrue() {
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
assertThat(mediaDataFilter.hasAnyMedia()).isTrue()
}
@Test
fun hasAnyMedia_recommendationSet_returnsFalse() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
}
@Test
fun hasAnyMediaOrRecommendation_noMediaSet_returnsFalse() {
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
}
@Test
fun hasAnyMediaOrRecommendation_mediaSet_returnsTrue() {
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
}
@Test
fun hasAnyMediaOrRecommendation_recommendationSet_returnsTrue() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isTrue()
}
@Test
fun hasActiveMedia_noMediaSet_returnsFalse() {
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
}
@Test
fun hasActiveMedia_inactiveMediaSet_returnsFalse() {
val data = dataMain.copy(active = false)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
}
@Test
fun hasActiveMedia_activeMediaSet_returnsTrue() {
val data = dataMain.copy(active = true)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
}
@Test
fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() {
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
}
@Test
fun hasActiveMediaOrRecommendation_inactiveMediaSet_returnsFalse() {
val data = dataMain.copy(active = false)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
}
@Test
fun hasActiveMediaOrRecommendation_activeMediaSet_returnsTrue() {
val data = dataMain.copy(active = true)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
}
@Test
fun hasActiveMediaOrRecommendation_inactiveRecommendationSet_returnsFalse() {
`when`(smartspaceData.isActive).thenReturn(false)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
}
@Test
fun hasActiveMediaOrRecommendation_invalidRecommendationSet_returnsFalse() {
`when`(smartspaceData.isValid()).thenReturn(false)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
}
@Test
fun hasActiveMediaOrRecommendation_activeAndValidRecommendationSet_returnsTrue() {
`when`(smartspaceData.isActive).thenReturn(true)
`when`(smartspaceData.isValid()).thenReturn(true)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
}
@Test
fun testHasAnyMediaOrRecommendation_onlyCurrentUser() {
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataGuest)
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
}
@Test
fun testHasActiveMediaOrRecommendation_onlyCurrentUser() {
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
val data = dataGuest.copy(active = true)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = data)
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
}
@Test
fun testOnNotificationRemoved_doesntHaveMedia() {
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
mediaDataFilter.onMediaDataRemoved(KEY)
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
}
@Test
fun testOnSwipeToDismiss_setsTimedOut() {
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
mediaDataFilter.onSwipeToDismiss()
verify(mediaDataManager).setTimedOut(eq(KEY), eq(true), eq(true))
}
@Test
fun testOnSmartspaceMediaDataLoaded_noMedia_activeValidRec_prioritizesSmartspace() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener)
.onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
verify(logger, never()).logRecommendationActivated(any(), any(), any())
}
@Test
fun testOnSmartspaceMediaDataLoaded_noMedia_inactiveRec_showsNothing() {
`when`(smartspaceData.isActive).thenReturn(false)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(),
anyInt(), anyBoolean())
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
verify(logger, never()).logRecommendationAdded(any(), any())
verify(logger, never()).logRecommendationActivated(any(), any(), any())
}
@Test
fun testOnSmartspaceMediaDataLoaded_noRecentMedia_activeValidRec_prioritizesSmartspace() {
val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener)
.onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
verify(logger, never()).logRecommendationActivated(any(), any(), any())
}
@Test
fun testOnSmartspaceMediaDataLoaded_noRecentMedia_inactiveRec_showsNothing() {
`when`(smartspaceData.isActive).thenReturn(false)
val dataOld = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
verify(logger, never()).logRecommendationAdded(any(), any())
verify(logger, never()).logRecommendationActivated(any(), any(), any())
}
@Test
fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_inactiveRec_showsNothing() {
`when`(smartspaceData.isActive).thenReturn(false)
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
eq(0), eq(false))
// AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// THEN we should tell listeners to treat the media as not active instead
verify(listener, never()).onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(),
anyInt(), anyBoolean())
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
verify(logger, never()).logRecommendationAdded(any(), any())
verify(logger, never()).logRecommendationActivated(any(), any(), any())
}
@Test
fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeInvalidRec_usesMedia() {
`when`(smartspaceData.isValid()).thenReturn(false)
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
eq(0), eq(false))
// AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// THEN we should tell listeners to treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
eq(100), eq(true))
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
// Smartspace update shouldn't be propagated for the empty rec list.
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
verify(logger, never()).logRecommendationAdded(any(), any())
verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
}
@Test
fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_activeValidRec_usesBoth() {
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
eq(0), eq(false))
// AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// THEN we should tell listeners to treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
eq(100), eq(true))
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isTrue()
// Smartspace update should also be propagated but not prioritized.
verify(listener)
.onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
}
@Test
fun testOnSmartspaceMediaDataRemoved_usedSmartspace_clearsSmartspace() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
}
@Test
fun testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() {
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
eq(0), eq(false))
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
eq(100), eq(true))
mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
assertThat(mediaDataFilter.hasActiveMediaOrRecommendation()).isFalse()
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
}
}