Merge "Fix Smartspace media logging bugs" into sc-v2-dev

This commit is contained in:
Jieru Shi
2021-12-01 18:57:51 +00:00
committed by Android (Google) Code Review
17 changed files with 288 additions and 199 deletions

View File

@@ -37,7 +37,7 @@ import javax.inject.Provider
private const val TAG = "MediaCarouselController" private const val TAG = "MediaCarouselController"
private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS) private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
private const val DEBUG = false private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
/** /**
* Class that is responsible for keeping the view carousel up to date. * Class that is responsible for keeping the view carousel up to date.
@@ -209,41 +209,56 @@ class MediaCarouselController @Inject constructor(
oldKey: String?, oldKey: String?,
data: MediaData, data: MediaData,
immediately: Boolean, immediately: Boolean,
isSsReactivated: Boolean receivedSmartspaceCardLatency: Int
) { ) {
if (addOrUpdatePlayer(key, oldKey, data)) { if (addOrUpdatePlayer(key, oldKey, data)) {
// Log card received if a new resumable media card is added // Log card received if a new resumable media card is added
MediaPlayerData.getMediaPlayer(key)?.let { MediaPlayerData.getMediaPlayer(key)?.let {
/* ktlint-disable max-line-length */
logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
it.mInstanceId, it.mInstanceId,
it.mUid, it.mUid,
/* isRecommendationCard */ false, /* isRecommendationCard */ false,
it.surfaceForSmartspaceLogging, intArrayOf(
SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
rank = MediaPlayerData.getMediaPlayerIndex(key)) rank = MediaPlayerData.getMediaPlayerIndex(key))
/* ktlint-disable max-line-length */
} }
} if (mediaCarouselScrollHandler.visibleToUser &&
if (isSsReactivated) { mediaCarouselScrollHandler.visibleMediaIndex
// If resumable media is reactivated by headphone connection, update instance == MediaPlayerData.getMediaPlayerIndex(key)) {
// id for each card and log a receive event. logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
}
} else if (receivedSmartspaceCardLatency != 0) {
// Log resume card received if resumable media card is reactivated and
// resume card is ranked first
MediaPlayerData.players().forEachIndexed { index, it -> MediaPlayerData.players().forEachIndexed { index, it ->
if (it.recommendationViewHolder == null) { if (it.recommendationViewHolder == null) {
it.mInstanceId = SmallHash.hash(it.mUid + it.mInstanceId = SmallHash.hash(it.mUid +
systemClock.currentTimeMillis().toInt()) systemClock.currentTimeMillis().toInt())
it.mIsImpressed = false
/* ktlint-disable max-line-length */
logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
it.mInstanceId, it.mInstanceId,
it.mUid, it.mUid,
/* isRecommendationCard */ false, /* isRecommendationCard */ false,
it.surfaceForSmartspaceLogging, intArrayOf(
rank = index) SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
rank = index,
receivedLatencyMillis = receivedSmartspaceCardLatency)
/* ktlint-disable max-line-length */
} }
} }
// If media container area already visible to the user, log impression for
// reactivated card.
if (mediaCarouselScrollHandler.visibleToUser &&
!mediaCarouselScrollHandler.qsExpanded) {
logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
}
} }
if (mediaCarouselScrollHandler.visibleToUser &&
isSsReactivated && !mediaCarouselScrollHandler.qsExpanded) {
// It could happen that reactived media player isn't visible to user because
// of it is a resumption card.
logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
}
val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
if (canRemove && !Utils.useMediaResumption(context)) { if (canRemove && !Utils.useMediaResumption(context)) {
// This view isn't playing, let's remove this! This happens e.g when // This view isn't playing, let's remove this! This happens e.g when
@@ -262,28 +277,51 @@ class MediaCarouselController @Inject constructor(
override fun onSmartspaceMediaDataLoaded( override fun onSmartspaceMediaDataLoaded(
key: String, key: String,
data: SmartspaceMediaData, data: SmartspaceMediaData,
shouldPrioritize: Boolean shouldPrioritize: Boolean,
isSsReactivated: Boolean
) { ) {
if (DEBUG) Log.d(TAG, "Loading Smartspace media update") if (DEBUG) Log.d(TAG, "Loading Smartspace media update")
if (data.isActive) { if (data.isActive) {
if (isSsReactivated && shouldPrioritize) {
// Log resume card received if resumable media card is reactivated and
// recommendation card is valid and ranked first
MediaPlayerData.players().forEachIndexed { index, it ->
if (it.recommendationViewHolder == null) {
it.mInstanceId = SmallHash.hash(it.mUid +
systemClock.currentTimeMillis().toInt())
it.mIsImpressed = false
/* ktlint-disable max-line-length */
logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
it.mInstanceId,
it.mUid,
/* isRecommendationCard */ false,
intArrayOf(
SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
rank = index,
receivedLatencyMillis = (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis).toInt())
/* ktlint-disable max-line-length */
}
}
}
addSmartspaceMediaRecommendations(key, data, shouldPrioritize) addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
MediaPlayerData.getMediaPlayer(key)?.let { MediaPlayerData.getMediaPlayer(key)?.let {
/* ktlint-disable max-line-length */
logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
it.mInstanceId, it.mInstanceId,
it.mUid, it.mUid,
/* isRecommendationCard */ true, /* isRecommendationCard */ true,
it.surfaceForSmartspaceLogging, intArrayOf(
rank = MediaPlayerData.getMediaPlayerIndex(key)) SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN),
if (mediaCarouselScrollHandler.visibleToUser && rank = MediaPlayerData.getMediaPlayerIndex(key),
mediaCarouselScrollHandler.visibleMediaIndex == receivedLatencyMillis = (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis).toInt())
MediaPlayerData.getMediaPlayerIndex(key)) { /* ktlint-disable max-line-length */
logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN }
it.mInstanceId, if (mediaCarouselScrollHandler.visibleToUser &&
it.mUid, mediaCarouselScrollHandler.visibleMediaIndex
/* isRecommendationCard */ true, == MediaPlayerData.getMediaPlayerIndex(key)) {
it.surfaceForSmartspaceLogging) logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
}
} }
} else { } else {
onSmartspaceMediaDataRemoved(data.targetId, immediately = true) onSmartspaceMediaDataRemoved(data.targetId, immediately = true)
@@ -718,7 +756,8 @@ class MediaCarouselController @Inject constructor(
mediaControlPanel.mInstanceId, mediaControlPanel.mInstanceId,
mediaControlPanel.mUid, mediaControlPanel.mUid,
isRecommendationCard, isRecommendationCard,
mediaControlPanel.surfaceForSmartspaceLogging) intArrayOf(mediaControlPanel.surfaceForSmartspaceLogging))
mediaControlPanel.mIsImpressed = true
} }
} }
@@ -731,12 +770,15 @@ class MediaCarouselController @Inject constructor(
* instanceId * instanceId
* @param uid uid for the application that media comes from * @param uid uid for the application that media comes from
* @param isRecommendationCard whether the card is media recommendation * @param isRecommendationCard whether the card is media recommendation
* @param surface which display surface the media card is on (e.g. lockscreen, shade) * @param surfaces list of display surfaces the media card is on (e.g. lockscreen, shade) when
* the event happened
* @param interactedSubcardRank the rank for interacted media item for recommendation card, -1 * @param interactedSubcardRank the rank for interacted media item for recommendation card, -1
* for tapping on card but not on any media item, 0 for first media item, 1 for second, etc. * for tapping on card but not on any media item, 0 for first media item, 1 for second, etc.
* @param interactedSubcardCardinality how many media items were shown to the user when there * @param interactedSubcardCardinality how many media items were shown to the user when there
* is user interaction * is user interaction
* @param rank the rank for media card in the media carousel, starting from 0 * @param rank the rank for media card in the media carousel, starting from 0
* @param receivedLatencyMillis latency in milliseconds for card received events. E.g. latency
* between headphone connection to sysUI displays media recommendation card
* *
*/ */
fun logSmartspaceCardReported( fun logSmartspaceCardReported(
@@ -744,10 +786,11 @@ class MediaCarouselController @Inject constructor(
instanceId: Int, instanceId: Int,
uid: Int, uid: Int,
isRecommendationCard: Boolean, isRecommendationCard: Boolean,
surface: Int, surfaces: IntArray,
interactedSubcardRank: Int = 0, interactedSubcardRank: Int = 0,
interactedSubcardCardinality: Int = 0, interactedSubcardCardinality: Int = 0,
rank: Int = mediaCarouselScrollHandler.visibleMediaIndex rank: Int = mediaCarouselScrollHandler.visibleMediaIndex,
receivedLatencyMillis: Int = 0
) { ) {
// Only log media resume card when Smartspace data is available // Only log media resume card when Smartspace data is available
if (!isRecommendationCard && if (!isRecommendationCard &&
@@ -756,50 +799,52 @@ class MediaCarouselController @Inject constructor(
return return
} }
/* ktlint-disable max-line-length */ val cardinality = mediaContent.getChildCount()
SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED, surfaces.forEach { surface ->
eventId, /* ktlint-disable max-line-length */
instanceId, SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
// Deprecated, replaced with AiAi feature type so we don't need to create logging eventId,
// card type for each new feature. instanceId,
SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD, // Deprecated, replaced with AiAi feature type so we don't need to create logging
surface, // card type for each new feature.
rank, SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD,
mediaContent.getChildCount(), surface,
if (isRecommendationCard) rank,
15 // MEDIA_RECOMMENDATION cardinality,
else if (isRecommendationCard)
31, // MEDIA_RESUME 15 // MEDIA_RECOMMENDATION
uid, else
interactedSubcardRank, 31, // MEDIA_RESUME
interactedSubcardCardinality, uid,
0 // received_latency_millis interactedSubcardRank,
) interactedSubcardCardinality,
/* ktlint-disable max-line-length */ receivedLatencyMillis
)
/* ktlint-disable max-line-length */
if (DEBUG) {
Log.d(TAG, "Log Smartspace card event id: $eventId instance id: $instanceId" +
" surface: $surface rank: $rank cardinality: $cardinality " +
"isRecommendationCard: $isRecommendationCard uid: $uid " +
"interactedSubcardRank: $interactedSubcardRank " +
"interactedSubcardCardinality: $interactedSubcardCardinality " +
"received_latency_millis: $receivedLatencyMillis")
}
}
} }
private fun onSwipeToDismiss() { private fun onSwipeToDismiss() {
val recommendation = MediaPlayerData.players().filter { MediaPlayerData.players().forEachIndexed {
it.recommendationViewHolder != null index, it ->
} if (it.mIsImpressed) {
// Use -1 as rank value to indicate user swipe to dismiss the card
if (!recommendation.isEmpty()) {
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
recommendation.get(0).mInstanceId,
recommendation.get(0).mUid,
true,
recommendation.get(0).surfaceForSmartspaceLogging,
rank = -1)
} else {
val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex
if (MediaPlayerData.players().size > visibleMediaIndex) {
val player = MediaPlayerData.players().elementAt(visibleMediaIndex)
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
player.mInstanceId, it.mInstanceId,
player.mUid, it.mUid,
false, it.recommendationViewHolder != null,
player.surfaceForSmartspaceLogging, intArrayOf(it.surfaceForSmartspaceLogging),
// Use -1 as rank value to indicate user swipe to dismiss the card
rank = -1) rank = -1)
// Reset card impressed state when swipe to dismissed
it.mIsImpressed = false
} }
} }
mediaManager.onSwipeToDismiss() mediaManager.onSwipeToDismiss()

View File

@@ -59,6 +59,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.util.animation.TransitionLayout; import com.android.systemui.util.animation.TransitionLayout;
import com.android.systemui.util.time.SystemClock;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.List; import java.util.List;
@@ -121,6 +122,9 @@ public class MediaControlPanel {
private MediaCarouselController mMediaCarouselController; private MediaCarouselController mMediaCarouselController;
private final MediaOutputDialogFactory mMediaOutputDialogFactory; private final MediaOutputDialogFactory mMediaOutputDialogFactory;
private final FalsingManager mFalsingManager; private final FalsingManager mFalsingManager;
// Used for swipe-to-dismiss logging.
protected boolean mIsImpressed = false;
private SystemClock mSystemClock;
/** /**
* Initialize a new control panel * Initialize a new control panel
@@ -134,7 +138,7 @@ public class MediaControlPanel {
SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager, SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager,
KeyguardDismissUtil keyguardDismissUtil, MediaOutputDialogFactory KeyguardDismissUtil keyguardDismissUtil, MediaOutputDialogFactory
mediaOutputDialogFactory, MediaCarouselController mediaCarouselController, mediaOutputDialogFactory, MediaCarouselController mediaCarouselController,
FalsingManager falsingManager) { FalsingManager falsingManager, SystemClock systemClock) {
mContext = context; mContext = context;
mBackgroundExecutor = backgroundExecutor; mBackgroundExecutor = backgroundExecutor;
mActivityStarter = activityStarter; mActivityStarter = activityStarter;
@@ -145,6 +149,8 @@ public class MediaControlPanel {
mMediaOutputDialogFactory = mediaOutputDialogFactory; mMediaOutputDialogFactory = mediaOutputDialogFactory;
mMediaCarouselController = mediaCarouselController; mMediaCarouselController = mediaCarouselController;
mFalsingManager = falsingManager; mFalsingManager = falsingManager;
mSystemClock = systemClock;
loadDimens(); loadDimens();
mSeekBarViewModel.setLogSmartspaceClick(() -> { mSeekBarViewModel.setLogSmartspaceClick(() -> {
@@ -291,7 +297,10 @@ public class MediaControlPanel {
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Unable to look up package name", e); Log.e(TAG, "Unable to look up package name", e);
} }
mInstanceId = SmallHash.hash(mUid); // Only assigns instance id if it's unassigned.
if (mInstanceId == -1) {
mInstanceId = SmallHash.hash(mUid + (int) mSystemClock.currentTimeMillis());
}
mBackgroundColor = data.getBackgroundColor(); mBackgroundColor = data.getBackgroundColor();
if (mToken == null || !mToken.equals(token)) { if (mToken == null || !mToken.equals(token)) {
@@ -885,7 +894,7 @@ public class MediaControlPanel {
mInstanceId, mInstanceId,
mUid, mUid,
isRecommendationCard, isRecommendationCard,
getSurfaceForSmartspaceLogging(), new int[]{getSurfaceForSmartspaceLogging()},
interactedSubcardRank, interactedSubcardRank,
interactedSubcardCardinality); interactedSubcardCardinality);
} }

View File

@@ -32,7 +32,7 @@ class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener,
oldKey: String?, oldKey: String?,
data: MediaData, data: MediaData,
immediately: Boolean, immediately: Boolean,
isSsReactivated: Boolean receivedSmartspaceCardLatency: Int
) { ) {
if (oldKey != null && oldKey != key && entries.contains(oldKey)) { if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
entries[key] = data to entries.remove(oldKey)?.second entries[key] = data to entries.remove(oldKey)?.second
@@ -46,7 +46,8 @@ class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener,
override fun onSmartspaceMediaDataLoaded( override fun onSmartspaceMediaDataLoaded(
key: String, key: String,
data: SmartspaceMediaData, data: SmartspaceMediaData,
shouldPrioritize: Boolean shouldPrioritize: Boolean,
isSsReactivated: Boolean
) { ) {
listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) } listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) }
} }

View File

@@ -56,7 +56,6 @@ internal val SMARTSPACE_MAX_AGE = SystemProperties
class MediaDataFilter @Inject constructor( class MediaDataFilter @Inject constructor(
private val context: Context, private val context: Context,
private val broadcastDispatcher: BroadcastDispatcher, private val broadcastDispatcher: BroadcastDispatcher,
private val mediaResumeListener: MediaResumeListener,
private val lockscreenUserManager: NotificationLockscreenUserManager, private val lockscreenUserManager: NotificationLockscreenUserManager,
@Main private val executor: Executor, @Main private val executor: Executor,
private val systemClock: SystemClock private val systemClock: SystemClock
@@ -88,7 +87,7 @@ class MediaDataFilter @Inject constructor(
oldKey: String?, oldKey: String?,
data: MediaData, data: MediaData,
immediately: Boolean, immediately: Boolean,
isSsReactivated: Boolean receivedSmartspaceCardLatency: Int
) { ) {
if (oldKey != null && oldKey != key) { if (oldKey != null && oldKey != key) {
allEntries.remove(oldKey) allEntries.remove(oldKey)
@@ -106,14 +105,15 @@ class MediaDataFilter @Inject constructor(
// Notify listeners // Notify listeners
listeners.forEach { listeners.forEach {
it.onMediaDataLoaded(key, oldKey, data, isSsReactivated = isSsReactivated) it.onMediaDataLoaded(key, oldKey, data)
} }
} }
override fun onSmartspaceMediaDataLoaded( override fun onSmartspaceMediaDataLoaded(
key: String, key: String,
data: SmartspaceMediaData, data: SmartspaceMediaData,
shouldPrioritize: Boolean shouldPrioritize: Boolean,
isSsReactivated: Boolean
) { ) {
if (!data.isActive) { if (!data.isActive) {
Log.d(TAG, "Inactive recommendation data. Skip triggering.") Log.d(TAG, "Inactive recommendation data. Skip triggering.")
@@ -123,8 +123,6 @@ class MediaDataFilter @Inject constructor(
// Override the pass-in value here, as the order of Smartspace card is only determined here. // Override the pass-in value here, as the order of Smartspace card is only determined here.
var shouldPrioritizeMutable = false var shouldPrioritizeMutable = false
smartspaceMediaData = data smartspaceMediaData = data
// Override the pass-in value here, as the Smartspace reactivation could only happen here.
var isSsReactivated = false
// Before forwarding the smartspace target, first check if we have recently inactive media // Before forwarding the smartspace target, first check if we have recently inactive media
val sorted = userEntries.toSortedMap(compareBy { val sorted = userEntries.toSortedMap(compareBy {
@@ -139,18 +137,25 @@ class MediaDataFilter @Inject constructor(
smartspaceMaxAgeMillis = TimeUnit.SECONDS.toMillis(smartspaceMaxAgeSeconds) smartspaceMaxAgeMillis = TimeUnit.SECONDS.toMillis(smartspaceMaxAgeSeconds)
} }
} }
val activeMedia = userEntries.filter { (key, value) -> value.active }
var isSsReactivatedMutable = activeMedia.isEmpty() && userEntries.isNotEmpty()
if (timeSinceActive < smartspaceMaxAgeMillis) { if (timeSinceActive < smartspaceMaxAgeMillis) {
val lastActiveKey = sorted.lastKey() // most recently active // It could happen there are existing active media resume cards, then we don't need to
// Notify listeners to consider this media active // reactivate.
Log.d(TAG, "reactivating $lastActiveKey instead of smartspace") if (isSsReactivatedMutable) {
reactivatedKey = lastActiveKey val lastActiveKey = sorted.lastKey() // most recently active
if (MediaPlayerData.firstActiveMediaIndex() == -1) { // Notify listeners to consider this media active
isSsReactivated = true Log.d(TAG, "reactivating $lastActiveKey instead of smartspace")
} reactivatedKey = lastActiveKey
val mediaData = sorted.get(lastActiveKey)!!.copy(active = true) val mediaData = sorted.get(lastActiveKey)!!.copy(active = true)
listeners.forEach { listeners.forEach {
it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData, it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData,
isSsReactivated = isSsReactivated) receivedSmartspaceCardLatency =
(systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis)
.toInt())
}
} }
} else { } else {
// Mark to prioritize Smartspace card if no recent media. // Mark to prioritize Smartspace card if no recent media.
@@ -161,7 +166,8 @@ class MediaDataFilter @Inject constructor(
Log.d(TAG, "Invalid recommendation data. Skip showing the rec card") Log.d(TAG, "Invalid recommendation data. Skip showing the rec card")
return return
} }
listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) } listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable,
isSsReactivatedMutable) }
} }
override fun onMediaDataRemoved(key: String) { override fun onMediaDataRemoved(key: String) {

View File

@@ -84,7 +84,7 @@ private val LOADING = MediaData(-1, false, 0, null, null, null, null, null,
emptyList(), emptyList(), "INVALID", null, null, null, true, null) emptyList(), emptyList(), "INVALID", null, null, null, true, null)
@VisibleForTesting @VisibleForTesting
internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData("INVALID", false, false, internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData("INVALID", false, false,
"INVALID", null, emptyList(), null, 0) "INVALID", null, emptyList(), null, 0, 0)
fun isMediaNotification(sbn: StatusBarNotification): Boolean { fun isMediaNotification(sbn: StatusBarNotification): Boolean {
return sbn.notification.isMediaNotification() return sbn.notification.isMediaNotification()
@@ -852,15 +852,16 @@ class MediaDataManager(
* until the next refresh-round before UI becomes visible. True by default to take in place * until the next refresh-round before UI becomes visible. True by default to take in place
* immediately. * immediately.
* *
* @param isSsReactivated indicates transition from a state with no active media players to * @param receivedSmartspaceCardLatency is the latency between headphone connects and sysUI
* a state with active media players upon receiving Smartspace media data. * displays Smartspace media targets. Will be 0 if the data is not activated by Smartspace
* signal.
*/ */
fun onMediaDataLoaded( fun onMediaDataLoaded(
key: String, key: String,
oldKey: String?, oldKey: String?,
data: MediaData, data: MediaData,
immediately: Boolean = true, immediately: Boolean = true,
isSsReactivated: Boolean = false receivedSmartspaceCardLatency: Int = 0
) {} ) {}
/** /**
@@ -869,11 +870,15 @@ class MediaDataManager(
* @param shouldPrioritize indicates the sorting priority of the Smartspace card. If true, * @param shouldPrioritize indicates the sorting priority of the Smartspace card. If true,
* it will be prioritized as the first card. Otherwise, it will show up as the last card as * it will be prioritized as the first card. Otherwise, it will show up as the last card as
* default. * default.
*
* @param isSsReactivated indicates resume media card is reactivated by Smartspace
* recommendation signal
*/ */
fun onSmartspaceMediaDataLoaded( fun onSmartspaceMediaDataLoaded(
key: String, key: String,
data: SmartspaceMediaData, data: SmartspaceMediaData,
shouldPrioritize: Boolean = false shouldPrioritize: Boolean = false,
isSsReactivated: Boolean = false
) {} ) {}
/** Called whenever a previously existing Media notification was removed. */ /** Called whenever a previously existing Media notification was removed. */
@@ -909,12 +914,13 @@ class MediaDataManager(
packageName(target)?.let { packageName(target)?.let {
return SmartspaceMediaData(target.smartspaceTargetId, isActive, true, it, return SmartspaceMediaData(target.smartspaceTargetId, isActive, true, it,
target.baseAction, target.iconGrid, target.baseAction, target.iconGrid,
dismissIntent, 0) dismissIntent, 0, target.creationTimeMillis)
} }
return EMPTY_SMARTSPACE_MEDIA_DATA return EMPTY_SMARTSPACE_MEDIA_DATA
.copy(targetId = target.smartspaceTargetId, .copy(targetId = target.smartspaceTargetId,
isActive = isActive, isActive = isActive,
dismissIntent = dismissIntent) dismissIntent = dismissIntent,
headphoneConnectionTimeMillis = target.creationTimeMillis)
} }
private fun packageName(target: SmartspaceTarget): String? { private fun packageName(target: SmartspaceTarget): String? {

View File

@@ -68,7 +68,7 @@ class MediaDeviceManager @Inject constructor(
oldKey: String?, oldKey: String?,
data: MediaData, data: MediaData,
immediately: Boolean, immediately: Boolean,
isSsReactivated: Boolean receivedSmartspaceCardLatency: Int
) { ) {
if (oldKey != null && oldKey != key) { if (oldKey != null && oldKey != key) {
val oldEntry = entries.remove(oldKey) val oldEntry = entries.remove(oldKey)
@@ -200,7 +200,7 @@ class MediaDeviceManager @Inject constructor(
@WorkerThread @WorkerThread
private fun updateCurrent() { private fun updateCurrent() {
val device = localMediaManager.currentConnectedDevice val device = localMediaManager.currentConnectedDevice
val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it)} val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) }
// If we have a controller but get a null route, then don't trust the device // If we have a controller but get a null route, then don't trust the device
val enabled = device != null && (controller == null || route != null) val enabled = device != null && (controller == null || route != null)

View File

@@ -61,7 +61,7 @@ class MediaHost constructor(
oldKey: String?, oldKey: String?,
data: MediaData, data: MediaData,
immediately: Boolean, immediately: Boolean,
isSsReactivated: Boolean receivedSmartspaceCardLatency: Int
) { ) {
if (immediately) { if (immediately) {
updateViewVisibility() updateViewVisibility()
@@ -71,7 +71,8 @@ class MediaHost constructor(
override fun onSmartspaceMediaDataLoaded( override fun onSmartspaceMediaDataLoaded(
key: String, key: String,
data: SmartspaceMediaData, data: SmartspaceMediaData,
shouldPrioritize: Boolean shouldPrioritize: Boolean,
isSsReactivated: Boolean
) { ) {
updateViewVisibility() updateViewVisibility()
} }

View File

@@ -184,7 +184,7 @@ class MediaResumeListener @Inject constructor(
oldKey: String?, oldKey: String?,
data: MediaData, data: MediaData,
immediately: Boolean, immediately: Boolean,
isSsReactivated: Boolean receivedSmartspaceCardLatency: Int
) { ) {
if (useMediaResumption) { if (useMediaResumption) {
// If this had been started from a resume state, disconnect now that it's live // If this had been started from a resume state, disconnect now that it's live

View File

@@ -96,7 +96,7 @@ class MediaSessionBasedFilter @Inject constructor(
oldKey: String?, oldKey: String?,
data: MediaData, data: MediaData,
immediately: Boolean, immediately: Boolean,
isSsReactivated: Boolean receivedSmartspaceCardLatency: Int
) { ) {
backgroundExecutor.execute { backgroundExecutor.execute {
data.token?.let { data.token?.let {
@@ -143,7 +143,8 @@ class MediaSessionBasedFilter @Inject constructor(
override fun onSmartspaceMediaDataLoaded( override fun onSmartspaceMediaDataLoaded(
key: String, key: String,
data: SmartspaceMediaData, data: SmartspaceMediaData,
shouldPrioritize: Boolean shouldPrioritize: Boolean,
isSsReactivated: Boolean
) { ) {
backgroundExecutor.execute { backgroundExecutor.execute {
dispatchSmartspaceMediaDataLoaded(key, data) dispatchSmartspaceMediaDataLoaded(key, data)

View File

@@ -63,7 +63,7 @@ class MediaTimeoutListener @Inject constructor(
oldKey: String?, oldKey: String?,
data: MediaData, data: MediaData,
immediately: Boolean, immediately: Boolean,
isSsReactivated: Boolean receivedSmartspaceCardLatency: Int
) { ) {
var reusedListener: PlaybackStateListener? = null var reusedListener: PlaybackStateListener? = null

View File

@@ -52,5 +52,9 @@ data class SmartspaceMediaData(
/** /**
* View's background color. * View's background color.
*/ */
val backgroundColor: Int val backgroundColor: Int,
/**
* The timestamp in milliseconds that headphone is connected.
*/
val headphoneConnectionTimeMillis: Long
) )

View File

@@ -242,12 +242,13 @@ public class NotificationMediaManager implements Dumpable {
@Override @Override
public void onMediaDataLoaded(@NonNull String key, public void onMediaDataLoaded(@NonNull String key,
@Nullable String oldKey, @NonNull MediaData data, boolean immediately, @Nullable String oldKey, @NonNull MediaData data, boolean immediately,
boolean isSsReactivated) { int receivedSmartspaceCardLatency) {
} }
@Override @Override
public void onSmartspaceMediaDataLoaded(@NonNull String key, public void onSmartspaceMediaDataLoaded(@NonNull String key,
@NonNull SmartspaceMediaData data, boolean shouldPrioritize) { @NonNull SmartspaceMediaData data, boolean shouldPrioritize,
boolean isSsReactivated) {
} }
@Override @Override
@@ -316,12 +317,13 @@ public class NotificationMediaManager implements Dumpable {
@Override @Override
public void onMediaDataLoaded(@NonNull String key, public void onMediaDataLoaded(@NonNull String key,
@Nullable String oldKey, @NonNull MediaData data, boolean immediately, @Nullable String oldKey, @NonNull MediaData data, boolean immediately,
boolean isSsReactivated) { int receivedSmartspaceCardLatency) {
} }
@Override @Override
public void onSmartspaceMediaDataLoaded(@NonNull String key, public void onSmartspaceMediaDataLoaded(@NonNull String key,
@NonNull SmartspaceMediaData data, boolean shouldPrioritize) { @NonNull SmartspaceMediaData data, boolean shouldPrioritize,
boolean isSsReactivated) {
} }

View File

@@ -123,6 +123,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
private lateinit var session: MediaSession private lateinit var session: MediaSession
private val device = MediaDeviceData(true, null, DEVICE_NAME) private val device = MediaDeviceData(true, null, DEVICE_NAME)
private val disabledDevice = MediaDeviceData(false, null, "Disabled Device") private val disabledDevice = MediaDeviceData(false, null, "Disabled Device")
private val clock = FakeSystemClock()
@JvmField @Rule val mockito = MockitoJUnit.rule() @JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -134,7 +135,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController, player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController,
seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil, seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil,
mediaOutputDialogFactory, mediaCarouselController, falsingManager) mediaOutputDialogFactory, mediaCarouselController, falsingManager, clock)
whenever(seekBarViewModel.progress).thenReturn(seekBarData) whenever(seekBarViewModel.progress).thenReturn(seekBarData)
// Mock out a view holder for the player to attach to. // Mock out a view holder for the player to attach to.

View File

@@ -18,6 +18,7 @@ package com.android.systemui.media;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq;
@@ -83,10 +84,10 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
public void eventNotEmittedWithoutDevice() { public void eventNotEmittedWithoutDevice() {
// WHEN data source emits an event without device data // WHEN data source emits an event without device data
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
// THEN an event isn't emitted // THEN an event isn't emitted
verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(),
anyBoolean()); anyInt());
} }
@Test @Test
@@ -95,7 +96,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
mManager.onMediaDeviceChanged(KEY, null, mDeviceData); mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN an event isn't emitted // THEN an event isn't emitted
verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(),
anyBoolean()); anyInt());
} }
@Test @Test
@@ -104,11 +105,11 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
mManager.onMediaDeviceChanged(KEY, null, mDeviceData); mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN media event is received // WHEN media event is received
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
// THEN the listener receives a combined event // THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(), verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(),
anyBoolean()); anyInt());
assertThat(captor.getValue().getDevice()).isNotNull(); assertThat(captor.getValue().getDevice()).isNotNull();
} }
@@ -116,13 +117,13 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
public void emitEventAfterMediaFirst() { public void emitEventAfterMediaFirst() {
// GIVEN that media event has already been received // GIVEN that media event has already been received
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
// WHEN device event is received // WHEN device event is received
mManager.onMediaDeviceChanged(KEY, null, mDeviceData); mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN the listener receives a combined event // THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(), verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(),
anyBoolean()); anyInt());
assertThat(captor.getValue().getDevice()).isNotNull(); assertThat(captor.getValue().getDevice()).isNotNull();
} }
@@ -130,16 +131,16 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
public void migrateKeyMediaFirst() { public void migrateKeyMediaFirst() {
// GIVEN that media and device info has already been received // GIVEN that media and device info has already been received
mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
reset(mListener); reset(mListener);
// WHEN a key migration event is received // WHEN a key migration event is received
mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
// THEN the listener receives a combined event // THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(), verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(),
anyBoolean()); anyInt());
assertThat(captor.getValue().getDevice()).isNotNull(); assertThat(captor.getValue().getDevice()).isNotNull();
} }
@@ -147,7 +148,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
public void migrateKeyDeviceFirst() { public void migrateKeyDeviceFirst() {
// GIVEN that media and device info has already been received // GIVEN that media and device info has already been received
mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
reset(mListener); reset(mListener);
// WHEN a key migration event is received // WHEN a key migration event is received
@@ -155,7 +156,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
// THEN the listener receives a combined event // THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(), verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(),
anyBoolean()); anyInt());
assertThat(captor.getValue().getDevice()).isNotNull(); assertThat(captor.getValue().getDevice()).isNotNull();
} }
@@ -163,17 +164,17 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
public void migrateKeyMediaAfter() { public void migrateKeyMediaAfter() {
// GIVEN that media and device info has already been received // GIVEN that media and device info has already been received
mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
reset(mListener); reset(mListener);
// WHEN a second key migration event is received for media // WHEN a second key migration event is received for media
mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
// THEN the key has already been migrated // THEN the key has already been migrated
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(), verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(),
anyBoolean()); anyInt());
assertThat(captor.getValue().getDevice()).isNotNull(); assertThat(captor.getValue().getDevice()).isNotNull();
} }
@@ -181,17 +182,17 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
public void migrateKeyDeviceAfter() { public void migrateKeyDeviceAfter() {
// GIVEN that media and device info has already been received // GIVEN that media and device info has already been received
mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
reset(mListener); reset(mListener);
// WHEN a second key migration event is received for the device // WHEN a second key migration event is received for the device
mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
// THEN the key has already be migrated // THEN the key has already be migrated
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(), verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(),
anyBoolean()); anyInt());
assertThat(captor.getValue().getDevice()).isNotNull(); assertThat(captor.getValue().getDevice()).isNotNull();
} }
@@ -206,7 +207,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
@Test @Test
public void mediaDataRemovedAfterMediaEvent() { public void mediaDataRemovedAfterMediaEvent() {
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDataRemoved(KEY); mManager.onMediaDataRemoved(KEY);
verify(mListener).onMediaDataRemoved(eq(KEY)); verify(mListener).onMediaDataRemoved(eq(KEY));
} }
@@ -222,14 +223,14 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
public void mediaDataKeyUpdated() { public void mediaDataKeyUpdated() {
// GIVEN that device and media events have already been received // GIVEN that device and media events have already been received
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
mManager.onMediaDeviceChanged(KEY, null, mDeviceData); mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN the key is changed // WHEN the key is changed
mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */, mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */,
false /* isSsReactivated */); 0 /* receivedSmartspaceCardLatency */);
// THEN the listener gets a load event with the correct keys // THEN the listener gets a load event with the correct keys
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded( verify(mListener).onMediaDataLoaded(
eq("NEW_KEY"), any(), captor.capture(), anyBoolean(), anyBoolean()); eq("NEW_KEY"), any(), captor.capture(), anyBoolean(), anyInt());
} }
} }

View File

@@ -87,7 +87,7 @@ class MediaDataFilterTest : SysuiTestCase() {
fun setup() { fun setup() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
MediaPlayerData.clear() MediaPlayerData.clear()
mediaDataFilter = MediaDataFilter(context, broadcastDispatcher, mediaResumeListener, mediaDataFilter = MediaDataFilter(context, broadcastDispatcher,
lockscreenUserManager, executor, clock) lockscreenUserManager, executor, clock)
mediaDataFilter.mediaDataManager = mediaDataManager mediaDataFilter.mediaDataManager = mediaDataManager
mediaDataFilter.addListener(listener) mediaDataFilter.addListener(listener)
@@ -107,6 +107,8 @@ class MediaDataFilterTest : SysuiTestCase() {
`when`(smartspaceData.isValid).thenReturn(true) `when`(smartspaceData.isValid).thenReturn(true)
`when`(smartspaceData.packageName).thenReturn(PACKAGE) `when`(smartspaceData.packageName).thenReturn(PACKAGE)
`when`(smartspaceData.recommendations).thenReturn(listOf(smartspaceMediaRecommendationItem)) `when`(smartspaceData.recommendations).thenReturn(listOf(smartspaceMediaRecommendationItem))
`when`(smartspaceData.headphoneConnectionTimeMillis).thenReturn(
clock.currentTimeMillis() - 100)
} }
private fun setUser(id: Int) { private fun setUser(id: Int) {
@@ -122,7 +124,7 @@ class MediaDataFilterTest : SysuiTestCase() {
// THEN we should tell the listener // THEN we should tell the listener
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -131,7 +133,7 @@ class MediaDataFilterTest : SysuiTestCase() {
mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest) mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
// THEN we should NOT tell the listener // THEN we should NOT tell the listener
verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean()) verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt())
} }
@Test @Test
@@ -178,11 +180,11 @@ class MediaDataFilterTest : SysuiTestCase() {
// THEN we should add back the guest user media // THEN we should add back the guest user media
verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true), verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true),
eq(false)) eq(0))
// but not the main user's // but not the main user's
verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean(), verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean(),
anyBoolean()) anyInt())
} }
@Test @Test
@@ -239,7 +241,8 @@ class MediaDataFilterTest : SysuiTestCase() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener) verify(listener)
.onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true)) .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true),
eq(false))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue() assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
} }
@@ -249,8 +252,9 @@ class MediaDataFilterTest : SysuiTestCase() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean()) verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt())
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean(),
anyBoolean())
assertThat(mediaDataFilter.hasActiveMedia()).isFalse() assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
} }
@@ -262,7 +266,8 @@ class MediaDataFilterTest : SysuiTestCase() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener) verify(listener)
.onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true)) .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true),
eq(true))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue() assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
} }
@@ -275,7 +280,8 @@ class MediaDataFilterTest : SysuiTestCase() {
clock.advanceTime(SMARTSPACE_MAX_AGE + 100) clock.advanceTime(SMARTSPACE_MAX_AGE + 100)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean(),
anyBoolean())
assertThat(mediaDataFilter.hasActiveMedia()).isFalse() assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
} }
@@ -287,15 +293,16 @@ class MediaDataFilterTest : SysuiTestCase() {
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
eq(false)) eq(0))
// AND we get a smartspace signal // AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// THEN we should tell listeners to treat the media as not active instead // THEN we should tell listeners to treat the media as not active instead
verify(listener, never()).onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(), verify(listener, never()).onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(),
anyInt())
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean(),
anyBoolean()) anyBoolean())
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
assertThat(mediaDataFilter.hasActiveMedia()).isFalse() assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
} }
@@ -307,7 +314,7 @@ class MediaDataFilterTest : SysuiTestCase() {
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
eq(false)) eq(0))
// AND we get a smartspace signal // AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -315,10 +322,11 @@ class MediaDataFilterTest : SysuiTestCase() {
// THEN we should tell listeners to treat the media as active instead // THEN we should tell listeners to treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true) val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
eq(true)) eq(100))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue() assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
// Smartspace update shouldn't be propagated for the empty rec list. // Smartspace update shouldn't be propagated for the empty rec list.
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean(),
anyBoolean())
} }
@Test @Test
@@ -327,7 +335,7 @@ class MediaDataFilterTest : SysuiTestCase() {
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
eq(false)) eq(0))
// AND we get a smartspace signal // AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -335,11 +343,12 @@ class MediaDataFilterTest : SysuiTestCase() {
// THEN we should tell listeners to treat the media as active instead // THEN we should tell listeners to treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true) val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
eq(true)) eq(100))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue() assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
// Smartspace update should also be propagated but not prioritized. // Smartspace update should also be propagated but not prioritized.
verify(listener) verify(listener)
.onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false),
eq(true))
} }
@Test @Test
@@ -356,13 +365,13 @@ class MediaDataFilterTest : SysuiTestCase() {
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
eq(false)) eq(0))
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
val dataCurrentAndActive = dataCurrent.copy(active = true) val dataCurrentAndActive = dataCurrent.copy(active = true)
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
eq(true)) eq(100))
mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)

View File

@@ -160,6 +160,7 @@ class MediaDataManagerTest : SysuiTestCase() {
whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE) whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE)
whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA) whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf(mediaRecommendationItem)) whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf(mediaRecommendationItem))
whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L)
} }
@After @After
@@ -213,7 +214,7 @@ class MediaDataManagerTest : SysuiTestCase() {
mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -225,7 +226,7 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
assertThat(mediaDataCaptor.value!!.active).isTrue() assertThat(mediaDataCaptor.value!!.active).isTrue()
} }
@@ -248,7 +249,7 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
assertThat(mediaDataCaptor.value!!.playbackLocation).isEqualTo( assertThat(mediaDataCaptor.value!!.playbackLocation).isEqualTo(
MediaData.PLAYBACK_CAST_REMOTE) MediaData.PLAYBACK_CAST_REMOTE)
} }
@@ -269,7 +270,7 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
val data = mediaDataCaptor.value val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse() assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
@@ -278,7 +279,7 @@ class MediaDataManagerTest : SysuiTestCase() {
// THEN the media data indicates that it is for resumption // THEN the media data indicates that it is for resumption
verify(listener) verify(listener)
.onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.resumption).isTrue()
assertThat(mediaDataCaptor.value.isPlaying).isFalse() assertThat(mediaDataCaptor.value.isPlaying).isFalse()
} }
@@ -293,7 +294,7 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(foregroundExecutor.runAllReady()).isEqualTo(2) assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
verify(listener) verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
val data = mediaDataCaptor.value val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse() assertThat(data.resumption).isFalse()
val resumableData = data.copy(resumeAction = Runnable {}) val resumableData = data.copy(resumeAction = Runnable {})
@@ -305,7 +306,7 @@ class MediaDataManagerTest : SysuiTestCase() {
// THEN the data is for resumption and the key is migrated to the package name // THEN the data is for resumption and the key is migrated to the package name
verify(listener) verify(listener)
.onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.resumption).isTrue()
verify(listener, never()).onMediaDataRemoved(eq(KEY)) verify(listener, never()).onMediaDataRemoved(eq(KEY))
// WHEN the second is removed // WHEN the second is removed
@@ -314,7 +315,7 @@ class MediaDataManagerTest : SysuiTestCase() {
verify(listener) verify(listener)
.onMediaDataLoaded( .onMediaDataLoaded(
eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true), eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.resumption).isTrue()
verify(listener).onMediaDataRemoved(eq(KEY_2)) verify(listener).onMediaDataRemoved(eq(KEY_2))
} }
@@ -329,7 +330,7 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
val data = mediaDataCaptor.value val data = mediaDataCaptor.value
val dataRemoteWithResume = data.copy(resumeAction = Runnable {}, val dataRemoteWithResume = data.copy(resumeAction = Runnable {},
playbackLocation = MediaData.PLAYBACK_CAST_LOCAL) playbackLocation = MediaData.PLAYBACK_CAST_LOCAL)
@@ -357,7 +358,7 @@ class MediaDataManagerTest : SysuiTestCase() {
// THEN the media data indicates that it is for resumption // THEN the media data indicates that it is for resumption
verify(listener) verify(listener)
.onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true), .onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
val data = mediaDataCaptor.value val data = mediaDataCaptor.value
assertThat(data.resumption).isTrue() assertThat(data.resumption).isTrue()
assertThat(data.song).isEqualTo(SESSION_TITLE) assertThat(data.song).isEqualTo(SESSION_TITLE)
@@ -405,7 +406,7 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener) verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -415,8 +416,8 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(KEY_MEDIA_SMARTSPACE), eq(KEY_MEDIA_SMARTSPACE),
eq(SmartspaceMediaData(KEY_MEDIA_SMARTSPACE, true /* isActive */, true /*isValid */, eq(SmartspaceMediaData(KEY_MEDIA_SMARTSPACE, true /* isActive */, true /*isValid */,
PACKAGE_NAME, mediaSmartspaceBaseAction, listOf(mediaRecommendationItem), PACKAGE_NAME, mediaSmartspaceBaseAction, listOf(mediaRecommendationItem),
DISMISS_INTENT, 0)), DISMISS_INTENT, 0, 1234L)),
eq(false)) eq(false), eq(false))
} }
@Test @Test
@@ -427,8 +428,9 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(KEY_MEDIA_SMARTSPACE), eq(KEY_MEDIA_SMARTSPACE),
eq(EMPTY_SMARTSPACE_MEDIA_DATA eq(EMPTY_SMARTSPACE_MEDIA_DATA
.copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true, .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true,
isValid = false, dismissIntent = DISMISS_INTENT)), isValid = false, dismissIntent = DISMISS_INTENT,
eq(false)) headphoneConnectionTimeMillis = 1234L)),
eq(false), eq(false))
} }
@Test @Test
@@ -447,15 +449,15 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(KEY_MEDIA_SMARTSPACE), eq(KEY_MEDIA_SMARTSPACE),
eq(EMPTY_SMARTSPACE_MEDIA_DATA eq(EMPTY_SMARTSPACE_MEDIA_DATA
.copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true, .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true,
isValid = false, dismissIntent = null)), isValid = false, dismissIntent = null, headphoneConnectionTimeMillis = 1234L)),
eq(false)) eq(false), eq(false))
} }
@Test @Test
fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_notCallsListener() { fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_notCallsListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf()) smartspaceMediaDataProvider.onTargetsAvailable(listOf())
verify(listener, never()) verify(listener, never())
.onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean()) .onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean(), anyBoolean())
} }
@Test @Test
@@ -479,7 +481,7 @@ class MediaDataManagerTest : SysuiTestCase() {
// THEN smartspace signal is ignored // THEN smartspace signal is ignored
verify(listener, never()) verify(listener, never())
.onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean()) .onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean(), anyBoolean())
} }
@Test @Test
@@ -487,7 +489,7 @@ class MediaDataManagerTest : SysuiTestCase() {
// GIVEN a media recommendation card is present // GIVEN a media recommendation card is present
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget)) smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
verify(listener).onSmartspaceMediaDataLoaded(eq(KEY_MEDIA_SMARTSPACE), anyObject(), verify(listener).onSmartspaceMediaDataLoaded(eq(KEY_MEDIA_SMARTSPACE), anyObject(),
anyBoolean()) anyBoolean(), anyBoolean())
// WHEN the media recommendation setting is turned off // WHEN the media recommendation setting is turned off
Settings.Secure.putInt(context.contentResolver, Settings.Secure.putInt(context.contentResolver,
@@ -507,7 +509,7 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
assertThat(mediaDataCaptor.value!!.lastActive).isAtLeast(currentTime) assertThat(mediaDataCaptor.value!!.lastActive).isAtLeast(currentTime)
} }
@@ -525,7 +527,7 @@ class MediaDataManagerTest : SysuiTestCase() {
// THEN the last active time is not changed // THEN the last active time is not changed
verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime) assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
} }
@@ -537,7 +539,7 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
val data = mediaDataCaptor.value val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse() assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
@@ -550,7 +552,7 @@ class MediaDataManagerTest : SysuiTestCase() {
// THEN the last active time is not changed // THEN the last active time is not changed
verify(listener) verify(listener)
.onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.resumption).isTrue()
assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime) assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
} }
@@ -577,7 +579,7 @@ class MediaDataManagerTest : SysuiTestCase() {
// THEN only the first MAX_COMPACT_ACTIONS are actually set // THEN only the first MAX_COMPACT_ACTIONS are actually set
verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
eq(false)) eq(0))
assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo( assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo(
MediaDataManager.MAX_COMPACT_ACTIONS) MediaDataManager.MAX_COMPACT_ACTIONS)
} }

View File

@@ -37,6 +37,7 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito import org.mockito.Mockito
import org.mockito.Mockito.any import org.mockito.Mockito.any
@@ -186,7 +187,7 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
bgExecutor.runAllReady() bgExecutor.runAllReady()
fgExecutor.runAllReady() fgExecutor.runAllReady()
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -209,7 +210,7 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is not filtered // THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -239,7 +240,7 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is not filtered // THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -255,14 +256,14 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is not filtered // THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
eq(false)) eq(0))
// WHEN a loaded event is received that matches the local session // WHEN a loaded event is received that matches the local session
filter.onMediaDataLoaded(KEY, null, mediaData2) filter.onMediaDataLoaded(KEY, null, mediaData2)
bgExecutor.runAllReady() bgExecutor.runAllReady()
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is filtered // THEN the event is filtered
verify(mediaListener, never()).onMediaDataLoaded( verify(mediaListener, never()).onMediaDataLoaded(
eq(KEY), eq(null), eq(mediaData2), anyBoolean(), anyBoolean()) eq(KEY), eq(null), eq(mediaData2), anyBoolean(), anyInt())
} }
@Test @Test
@@ -279,7 +280,7 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
// THEN the event is not filtered because there isn't a notification for the remote // THEN the event is not filtered because there isn't a notification for the remote
// session. // session.
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -297,14 +298,14 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is not filtered // THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true), verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true),
eq(false)) eq(0))
// WHEN a loaded event is received that matches the local session // WHEN a loaded event is received that matches the local session
filter.onMediaDataLoaded(key2, null, mediaData2) filter.onMediaDataLoaded(key2, null, mediaData2)
bgExecutor.runAllReady() bgExecutor.runAllReady()
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is filtered // THEN the event is filtered
verify(mediaListener, never()) verify(mediaListener, never())
.onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean()) .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyInt())
// AND there should be a removed event for key2 // AND there should be a removed event for key2
verify(mediaListener).onMediaDataRemoved(eq(key2)) verify(mediaListener).onMediaDataRemoved(eq(key2))
} }
@@ -324,14 +325,14 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is not filtered // THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true), verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true),
eq(false)) eq(0))
// WHEN a loaded event is received that matches the remote session // WHEN a loaded event is received that matches the remote session
filter.onMediaDataLoaded(key2, null, mediaData2) filter.onMediaDataLoaded(key2, null, mediaData2)
bgExecutor.runAllReady() bgExecutor.runAllReady()
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is not filtered // THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true), verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -348,14 +349,14 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is not filtered // THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
eq(false)) eq(0))
// WHEN a loaded event is received that matches the local session // WHEN a loaded event is received that matches the local session
filter.onMediaDataLoaded(KEY, null, mediaData2) filter.onMediaDataLoaded(KEY, null, mediaData2)
bgExecutor.runAllReady() bgExecutor.runAllReady()
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is not filtered // THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true), verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -374,7 +375,7 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the event is not filtered // THEN the event is not filtered
verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -397,7 +398,7 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the key migration event is fired // THEN the key migration event is fired
verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true), verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true),
eq(false)) eq(0))
} }
@Test @Test
@@ -427,13 +428,13 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() {
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the key migration event is filtered // THEN the key migration event is filtered
verify(mediaListener, never()) verify(mediaListener, never())
.onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean()) .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyInt())
// WHEN a loaded event is received that matches the remote session // WHEN a loaded event is received that matches the remote session
filter.onMediaDataLoaded(key2, null, mediaData1) filter.onMediaDataLoaded(key2, null, mediaData1)
bgExecutor.runAllReady() bgExecutor.runAllReady()
fgExecutor.runAllReady() fgExecutor.runAllReady()
// THEN the key migration event is fired // THEN the key migration event is fired
verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true), verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true),
eq(false)) eq(0))
} }
} }