summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Beth Thibodeau <ethibodeau@google.com> 2022-04-11 15:36:17 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-04-11 15:36:17 +0000
commitf30664e60b72c8f723ca604104c96b24fbb63c68 (patch)
treeebf9c0fd384f9305b10e1a8af620ea4b5cd63137
parent2211b3599275d65ec71f02577f68b1f52bc74274 (diff)
parentf000c437a8a46ae736f47c7c7af3d529641c077c (diff)
Merge "Add logging for media recommendation events" into tm-dev
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt203
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt49
10 files changed, 367 insertions, 60 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index a636dc5e6ae1..8e81831a8dc3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -508,6 +508,11 @@ class MediaCarouselController @Inject constructor(
dismissMediaData: Boolean = true,
dismissRecommendation: Boolean = true
) {
+ if (key == MediaPlayerData.smartspaceMediaKey()) {
+ MediaPlayerData.smartspaceMediaData?.let {
+ logger.logRecommendationRemoved(it.packageName, it.instanceId)
+ }
+ }
val removed = MediaPlayerData.removeMediaPlayer(key)
removed?.apply {
mediaCarouselScrollHandler.onPrePlayerRemoved(removed)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 8ddec5c7d9f7..c95678311093 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -331,6 +331,7 @@ public class MediaControlPanel {
});
mRecommendationViewHolder.getSettings().setOnClickListener(v -> {
if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ mLogger.logLongPressSettings(mUid, mPackageName, mInstanceId);
mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
}
});
@@ -940,6 +941,8 @@ public class MediaControlPanel {
mSmartspaceId = SmallHash.hash(data.getTargetId());
mBackgroundColor = data.getBackgroundColor();
+ mPackageName = data.getPackageName();
+ mInstanceId = data.getInstanceId();
TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
recommendationCard.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
@@ -1068,6 +1071,7 @@ public class MediaControlPanel {
mRecommendationViewHolder.getDismiss().setOnClickListener(v -> {
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
+ mLogger.logLongPressDismiss(mUid, mPackageName, mInstanceId);
logSmartspaceCardReported(
761 // SMARTSPACE_CARD_DISMISS
);
@@ -1204,6 +1208,11 @@ public class MediaControlPanel {
view.setOnClickListener(v -> {
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
+ if (interactedSubcardRank == -1) {
+ mLogger.logRecommendationCardTap(mPackageName, mInstanceId);
+ } else {
+ mLogger.logRecommendationItemTap(mPackageName, mInstanceId, interactedSubcardRank);
+ }
logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
interactedSubcardRank,
getSmartspaceSubCardCardinality());
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 80a407b9d90f..647d3efa5916 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -60,7 +60,8 @@ class MediaDataFilter @Inject constructor(
private val broadcastSender: BroadcastSender,
private val lockscreenUserManager: NotificationLockscreenUserManager,
@Main private val executor: Executor,
- private val systemClock: SystemClock
+ private val systemClock: SystemClock,
+ private val logger: MediaUiEventLogger
) : MediaDataManager.Listener {
private val userTracker: CurrentUserTracker
private val _listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
@@ -151,6 +152,8 @@ class MediaDataFilter @Inject constructor(
Log.d(TAG, "reactivating $lastActiveKey instead of smartspace")
reactivatedKey = lastActiveKey
val mediaData = sorted.get(lastActiveKey)!!.copy(active = true)
+ logger.logRecommendationActivated(mediaData.appUid, mediaData.packageName,
+ mediaData.instanceId)
listeners.forEach {
it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData,
receivedSmartspaceCardLatency =
@@ -167,6 +170,8 @@ class MediaDataFilter @Inject constructor(
Log.d(TAG, "Invalid recommendation data. Skip showing the rec card")
return
}
+ logger.logRecommendationAdded(smartspaceMediaData.packageName,
+ smartspaceMediaData.instanceId)
listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
}
@@ -197,7 +202,9 @@ class MediaDataFilter @Inject constructor(
if (smartspaceMediaData.isActive) {
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
- targetId = smartspaceMediaData.targetId, isValid = smartspaceMediaData.isValid)
+ targetId = smartspaceMediaData.targetId,
+ isValid = smartspaceMediaData.isValid,
+ instanceId = smartspaceMediaData.instanceId)
}
listeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) }
}
@@ -252,9 +259,12 @@ class MediaDataFilter @Inject constructor(
broadcastSender.sendBroadcast(dismissIntent)
}
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
- targetId = smartspaceMediaData.targetId, isValid = smartspaceMediaData.isValid)
+ targetId = smartspaceMediaData.targetId,
+ isValid = smartspaceMediaData.isValid,
+ instanceId = smartspaceMediaData.instanceId)
+ mediaDataManager.dismissSmartspaceRecommendation(smartspaceMediaData.targetId,
+ delay = 0L)
}
- mediaDataManager.dismissSmartspaceRecommendation(smartspaceMediaData.targetId, delay = 0L)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 9a2977d56153..0ad15facee66 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -103,8 +103,17 @@ private val LOADING = MediaData(
appUid = Process.INVALID_UID)
@VisibleForTesting
-internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData("INVALID", false, false,
- "INVALID", null, emptyList(), null, 0, 0)
+internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData(
+ targetId = "INVALID",
+ isActive = false,
+ isValid = false,
+ packageName = "INVALID",
+ cardAction = null,
+ recommendations = emptyList(),
+ dismissIntent = null,
+ backgroundColor = 0,
+ headphoneConnectionTimeMillis = 0,
+ instanceId = InstanceId.fakeInstanceId(-1))
fun isMediaNotification(sbn: StatusBarNotification): Boolean {
return sbn.notification.isMediaNotification()
@@ -532,14 +541,16 @@ class MediaDataManager(
* connection session.
*/
fun dismissSmartspaceRecommendation(key: String, delay: Long) {
- if (smartspaceMediaData.targetId != key) {
+ if (smartspaceMediaData.targetId != key || !smartspaceMediaData.isValid) {
+ // If this doesn't match, or we've already invalidated the data, no action needed
return
}
if (DEBUG) Log.d(TAG, "Dismissing Smartspace media target")
if (smartspaceMediaData.isActive) {
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
- targetId = smartspaceMediaData.targetId)
+ targetId = smartspaceMediaData.targetId,
+ instanceId = smartspaceMediaData.instanceId)
}
foregroundExecutor.executeDelayed(
{ notifySmartspaceMediaDataRemoved(
@@ -1035,7 +1046,8 @@ class MediaDataManager(
Log.d(TAG, "Set Smartspace media to be inactive for the data update")
}
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
- targetId = smartspaceMediaData.targetId)
+ targetId = smartspaceMediaData.targetId,
+ instanceId = smartspaceMediaData.instanceId)
notifySmartspaceMediaDataRemoved(smartspaceMediaData.targetId, immediately = false)
}
1 -> {
@@ -1214,15 +1226,24 @@ class MediaDataManager(
.getParcelable(EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY) as Intent?
}
packageName(target)?.let {
- return SmartspaceMediaData(target.smartspaceTargetId, isActive, true, it,
- target.baseAction, target.iconGrid,
- dismissIntent, 0, target.creationTimeMillis)
+ return SmartspaceMediaData(
+ targetId = target.smartspaceTargetId,
+ isActive = isActive,
+ isValid = true,
+ packageName = it,
+ cardAction = target.baseAction,
+ recommendations = target.iconGrid,
+ dismissIntent = dismissIntent,
+ backgroundColor = 0,
+ headphoneConnectionTimeMillis = target.creationTimeMillis,
+ instanceId = logger.getNewInstanceId())
}
return EMPTY_SMARTSPACE_MEDIA_DATA
.copy(targetId = target.smartspaceTargetId,
isActive = isActive,
dismissIntent = dismissIntent,
- headphoneConnectionTimeMillis = target.creationTimeMillis)
+ headphoneConnectionTimeMillis = target.creationTimeMillis,
+ instanceId = logger.getNewInstanceId())
}
private fun packageName(target: SmartspaceTarget): String? {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt
index 3eba3b55b7e8..52f5cc568ba4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt
@@ -151,6 +151,31 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger)
}
logger.log(event)
}
+
+ fun logRecommendationAdded(packageName: String, instanceId: InstanceId) {
+ logger.logWithInstanceId(MediaUiEvent.MEDIA_RECOMMENDATION_ADDED, 0, packageName,
+ instanceId)
+ }
+
+ fun logRecommendationRemoved(packageName: String, instanceId: InstanceId) {
+ logger.logWithInstanceId(MediaUiEvent.MEDIA_RECOMMENDATION_REMOVED, 0, packageName,
+ instanceId)
+ }
+
+ fun logRecommendationActivated(uid: Int, packageName: String, instanceId: InstanceId) {
+ logger.logWithInstanceId(MediaUiEvent.MEDIA_RECOMMENDATION_ACTIVATED, uid, packageName,
+ instanceId)
+ }
+
+ fun logRecommendationItemTap(packageName: String, instanceId: InstanceId, position: Int) {
+ logger.logWithInstanceIdAndPosition(MediaUiEvent.MEDIA_RECOMMENDATION_ITEM_TAP, 0,
+ packageName, instanceId, position)
+ }
+
+ fun logRecommendationCardTap(packageName: String, instanceId: InstanceId) {
+ logger.logWithInstanceId(MediaUiEvent.MEDIA_RECOMMENDATION_CARD_TAP, 0, packageName,
+ instanceId)
+ }
}
enum class MediaUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum {
@@ -233,7 +258,22 @@ enum class MediaUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum {
MEDIA_CAROUSEL_LOCATION_LOCKSCREEN(1039),
@UiEvent(doc = "The media carousel moved to the dream state")
- MEDIA_CAROUSEL_LOCATION_DREAM(1040);
+ MEDIA_CAROUSEL_LOCATION_DREAM(1040),
+
+ @UiEvent(doc = "A media recommendation card was added to the media carousel")
+ MEDIA_RECOMMENDATION_ADDED(1041),
+
+ @UiEvent(doc = "A media recommendation card was removed from the media carousel")
+ MEDIA_RECOMMENDATION_REMOVED(1042),
+
+ @UiEvent(doc = "An existing media control was made active as a recommendation")
+ MEDIA_RECOMMENDATION_ACTIVATED(1043),
+
+ @UiEvent(doc = "User tapped on an item in a media recommendation card")
+ MEDIA_RECOMMENDATION_ITEM_TAP(1044),
+
+ @UiEvent(doc = "User tapped on a media recommendation card")
+ MEDIA_RECOMMENDATION_CARD_TAP(1045);
override fun getId() = metricId
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
index 066a6fbfa8be..e161ea7a9c68 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
@@ -18,6 +18,7 @@ package com.android.systemui.media
import android.app.smartspace.SmartspaceAction
import android.content.Intent
+import com.android.internal.logging.InstanceId
/** State of a Smartspace media recommendations view. */
data class SmartspaceMediaData(
@@ -56,5 +57,9 @@ data class SmartspaceMediaData(
/**
* The timestamp in milliseconds that headphone is connected.
*/
- val headphoneConnectionTimeMillis: Long
+ val headphoneConnectionTimeMillis: Long,
+ /**
+ * Instance ID for [MediaUiEventLogger]
+ */
+ val instanceId: InstanceId
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
index 67fe0445e225..1522ee8cd6d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.media
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.qualifiers.Main
@@ -28,6 +29,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
@@ -225,4 +227,19 @@ class MediaCarouselControllerTest : SysuiTestCase() {
animate = false)
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_DREAM_OVERLAY)
}
+
+ @Test
+ fun testRecommendationRemoved_logged() {
+ val packageName = "smartspace package"
+ val instanceId = InstanceId.fakeInstanceId(123)
+
+ val smartspaceData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
+ packageName = packageName,
+ instanceId = instanceId
+ )
+ MediaPlayerData.addMediaRecommendation(SMARTSPACE_KEY, smartspaceData, panel, true, clock)
+ mediaCarouselController.removePlayer(SMARTSPACE_KEY)
+
+ verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 14db4bf81e14..a58a28e3920b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -17,16 +17,22 @@
package com.android.systemui.media
import android.app.PendingIntent
+import android.app.smartspace.SmartspaceAction
+import android.content.Context
import org.mockito.Mockito.`when` as whenever
import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
import android.graphics.Color
import android.graphics.drawable.Animatable2
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.Icon
import android.graphics.drawable.RippleDrawable
import android.media.MediaMetadata
import android.media.session.MediaSession
import android.media.session.PlaybackState
+import android.os.Bundle
import android.os.Handler
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
import android.testing.AndroidTestingRunner
@@ -67,6 +73,7 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
@@ -107,6 +114,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var collapsedSet: ConstraintSet
@Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
@Mock private lateinit var mediaCarouselController: MediaCarouselController
+ @Mock private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
@Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var transitionParent: ViewGroup
private lateinit var appIcon: ImageView
@@ -145,6 +153,14 @@ public class MediaControlPanelTest : SysuiTestCase() {
private val clock = FakeSystemClock()
@Mock private lateinit var logger: MediaUiEventLogger
@Mock private lateinit var instanceId: InstanceId
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var applicationInfo: ApplicationInfo
+
+ @Mock private lateinit var recommendationViewHolder: RecommendationViewHolder
+ @Mock private lateinit var smartspaceAction: SmartspaceAction
+ private lateinit var smartspaceData: SmartspaceMediaData
+ @Mock private lateinit var coverContainer: ViewGroup
+ private lateinit var coverItem: ImageView
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -155,6 +171,16 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(mediaViewController.expandedLayout).thenReturn(expandedSet)
whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet)
+ // Set up package manager mocks
+ val icon = context.getDrawable(R.drawable.ic_android)
+ whenever(packageManager.getApplicationIcon(anyString())).thenReturn(icon)
+ whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java)))
+ .thenReturn(icon)
+ whenever(packageManager.getApplicationInfo(eq(PACKAGE), anyInt()))
+ .thenReturn(applicationInfo)
+ whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE)
+ context.setMockPackageManager(packageManager)
+
player = MediaControlPanel(
context,
bgExecutor,
@@ -170,7 +196,60 @@ public class MediaControlPanelTest : SysuiTestCase() {
clock,
logger
)
+
+ initMediaViewHolderMocks()
+
+ // Create media session
+ val metadataBuilder = MediaMetadata.Builder().apply {
+ putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
+ putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
+ }
+ val playbackBuilder = PlaybackState.Builder().apply {
+ setState(PlaybackState.STATE_PAUSED, 6000L, 1f)
+ setActions(PlaybackState.ACTION_PLAY)
+ }
+ session = MediaSession(context, SESSION_KEY).apply {
+ setMetadata(metadataBuilder.build())
+ setPlaybackState(playbackBuilder.build())
+ }
+ session.setActive(true)
+
+ mediaData = MediaTestUtils.emptyMediaData.copy(
+ backgroundColor = BG_COLOR,
+ artist = ARTIST,
+ song = TITLE,
+ packageName = PACKAGE,
+ token = session.sessionToken,
+ device = device,
+ instanceId = instanceId)
+
+ // Set up recommendation view
+ initRecommendationViewHolderMocks()
+
+ // Set valid recommendation data
+ val extras = Bundle()
+ val intent = Intent().apply {
+ putExtras(extras)
+ setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ whenever(smartspaceAction.intent).thenReturn(intent)
+ whenever(smartspaceAction.extras).thenReturn(extras)
+ smartspaceData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
+ packageName = PACKAGE,
+ instanceId = instanceId,
+ recommendations = listOf(smartspaceAction),
+ cardAction = smartspaceAction
+ )
+ }
+
+ /**
+ * Initialize elements in media view holder
+ */
+ private fun initMediaViewHolderMocks() {
whenever(seekBarViewModel.progress).thenReturn(seekBarData)
+ whenever(mediaCarouselController.mediaCarouselScrollHandler)
+ .thenReturn(mediaCarouselScrollHandler)
+ whenever(mediaCarouselScrollHandler.qsExpanded).thenReturn(false)
// Set up mock views for the players
appIcon = ImageView(context)
@@ -218,37 +297,6 @@ public class MediaControlPanelTest : SysuiTestCase() {
action4.id)
}
- initMediaViewHolderMocks()
-
- // Create media session
- val metadataBuilder = MediaMetadata.Builder().apply {
- putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
- putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
- }
- val playbackBuilder = PlaybackState.Builder().apply {
- setState(PlaybackState.STATE_PAUSED, 6000L, 1f)
- setActions(PlaybackState.ACTION_PLAY)
- }
- session = MediaSession(context, SESSION_KEY).apply {
- setMetadata(metadataBuilder.build())
- setPlaybackState(playbackBuilder.build())
- }
- session.setActive(true)
-
- mediaData = MediaTestUtils.emptyMediaData.copy(
- backgroundColor = BG_COLOR,
- artist = ARTIST,
- song = TITLE,
- packageName = PACKAGE,
- token = session.sessionToken,
- device = device,
- instanceId = instanceId)
- }
-
- /**
- * Initialize elements in media view holder
- */
- private fun initMediaViewHolderMocks() {
whenever(viewHolder.player).thenReturn(view)
whenever(viewHolder.appIcon).thenReturn(appIcon)
whenever(viewHolder.albumView).thenReturn(albumView)
@@ -297,6 +345,38 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.actionsTopBarrier).thenReturn(actionsTopBarrier)
}
+ /**
+ * Initialize elements for the recommendation view holder
+ */
+ private fun initRecommendationViewHolderMocks() {
+ whenever(recommendationViewHolder.recommendations).thenReturn(view)
+ whenever(recommendationViewHolder.cardIcon).thenReturn(appIcon)
+ whenever(recommendationViewHolder.cardText).thenReturn(titleText)
+
+ // Add a recommendation item
+ coverItem = ImageView(context).also { it.setId(R.id.media_cover1) }
+ whenever(coverContainer.id).thenReturn(R.id.media_cover1_container)
+ whenever(recommendationViewHolder.mediaCoverItems).thenReturn(listOf(coverItem))
+ whenever(recommendationViewHolder.mediaCoverContainers).thenReturn(listOf(coverContainer))
+ whenever(recommendationViewHolder.mediaCoverItemsResIds)
+ .thenReturn(listOf(R.id.media_cover1))
+ whenever(recommendationViewHolder.mediaCoverContainersResIds)
+ .thenReturn(listOf(R.id.media_cover1_container))
+
+ // Long press menu
+ whenever(recommendationViewHolder.settings).thenReturn(settings)
+ whenever(recommendationViewHolder.cancel).thenReturn(cancel)
+ whenever(recommendationViewHolder.dismiss).thenReturn(dismiss)
+
+ val actionIcon = Icon.createWithResource(context, R.drawable.ic_android)
+ whenever(smartspaceAction.icon).thenReturn(actionIcon)
+
+ // Needed for card and item action click
+ val mockContext = mock(Context::class.java)
+ whenever(view.context).thenReturn(mockContext)
+ whenever(coverContainer.context).thenReturn(mockContext)
+ }
+
@After
fun tearDown() {
session.release()
@@ -923,6 +1003,67 @@ public class MediaControlPanelTest : SysuiTestCase() {
verify(logger).logSeek(anyInt(), eq(PACKAGE), eq(instanceId))
}
+ @Test
+ fun recommendation_gutsClosed_longPressOpens() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+ whenever(mediaViewController.isGutsVisible).thenReturn(false)
+
+ val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+ verify(recommendationViewHolder.recommendations).setOnLongClickListener(captor.capture())
+
+ captor.value.onLongClick(recommendationViewHolder.recommendations)
+ verify(mediaViewController).openGuts()
+ verify(logger).logLongPressOpen(anyInt(), eq(PACKAGE), eq(instanceId))
+ }
+
+ @Test
+ fun recommendation_settingsButtonClick_isLogged() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ settings.callOnClick()
+ verify(logger).logLongPressSettings(anyInt(), eq(PACKAGE), eq(instanceId))
+
+ val captor = ArgumentCaptor.forClass(Intent::class.java)
+ verify(activityStarter).startActivity(captor.capture(), eq(true))
+
+ assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS)
+ }
+
+ @Test
+ fun recommendation_dismissButton_isLogged() {
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ dismiss.callOnClick()
+ verify(logger).logLongPressDismiss(anyInt(), eq(PACKAGE), eq(instanceId))
+ }
+
+ @Test
+ fun recommendation_tapOnCard_isLogged() {
+ val captor = ArgumentCaptor.forClass(View.OnClickListener::class.java)
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ verify(recommendationViewHolder.recommendations).setOnClickListener(captor.capture())
+ captor.value.onClick(recommendationViewHolder.recommendations)
+
+ verify(logger).logRecommendationCardTap(eq(PACKAGE), eq(instanceId))
+ }
+
+ @Test
+ fun recommendation_tapOnItem_isLogged() {
+ val captor = ArgumentCaptor.forClass(View.OnClickListener::class.java)
+ player.attachRecommendation(recommendationViewHolder)
+ player.bindRecommendation(smartspaceData)
+
+ verify(coverContainer).setOnClickListener(captor.capture())
+ captor.value.onClick(recommendationViewHolder.recommendations)
+
+ verify(logger).logRecommendationItemTap(eq(PACKAGE), eq(instanceId), eq(0))
+ }
+
private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener =
withArgCaptor { verify(seekBarViewModel).setScrubbingChangeListener(capture()) }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index 3b996d4a37d3..1f9490ab3851 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -20,6 +20,7 @@ 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
@@ -46,7 +47,11 @@ 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)
@@ -69,6 +74,8 @@ class MediaDataFilterTest : SysuiTestCase() {
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
@@ -79,8 +86,14 @@ class MediaDataFilterTest : SysuiTestCase() {
fun setup() {
MockitoAnnotations.initMocks(this)
MediaPlayerData.clear()
- mediaDataFilter = MediaDataFilter(context, broadcastDispatcher, broadcastSender,
- lockscreenUserManager, executor, clock)
+ mediaDataFilter = MediaDataFilter(
+ context,
+ broadcastDispatcher,
+ broadcastSender,
+ lockscreenUserManager,
+ executor,
+ clock,
+ logger)
mediaDataFilter.mediaDataManager = mediaDataManager
mediaDataFilter.addListener(listener)
@@ -90,16 +103,19 @@ class MediaDataFilterTest : SysuiTestCase() {
// Set up test media data
dataMain = MediaTestUtils.emptyMediaData.copy(
userId = USER_MAIN,
- packageName = PACKAGE)
+ 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(PACKAGE)
+ `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) {
@@ -241,6 +257,8 @@ class MediaDataFilterTest : SysuiTestCase() {
.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
@@ -254,6 +272,8 @@ class MediaDataFilterTest : SysuiTestCase() {
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
@@ -267,6 +287,8 @@ class MediaDataFilterTest : SysuiTestCase() {
.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
@@ -281,6 +303,8 @@ class MediaDataFilterTest : SysuiTestCase() {
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
@@ -302,6 +326,8 @@ class MediaDataFilterTest : SysuiTestCase() {
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
@@ -324,6 +350,8 @@ class MediaDataFilterTest : SysuiTestCase() {
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
@@ -345,6 +373,8 @@ class MediaDataFilterTest : SysuiTestCase() {
// 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
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 68eebede1482..858249960a6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -19,6 +19,7 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.media.utils.MediaConstants
import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
import com.android.systemui.InstanceIdSequenceFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
@@ -47,6 +48,7 @@ import org.mockito.Mockito
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
@@ -452,11 +454,22 @@ class MediaDataManagerTest : SysuiTestCase() {
@Test
fun testOnSmartspaceMediaDataLoaded_hasNewValidMediaTarget_callsListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+ verify(logger).getNewInstanceId()
+ val instanceId = instanceIdSequence.lastInstanceId
+
verify(listener).onSmartspaceMediaDataLoaded(
eq(KEY_MEDIA_SMARTSPACE),
- eq(SmartspaceMediaData(KEY_MEDIA_SMARTSPACE, true /* isActive */, true /*isValid */,
- PACKAGE_NAME, mediaSmartspaceBaseAction, listOf(mediaRecommendationItem),
- DISMISS_INTENT, 0, 1234L)),
+ eq(SmartspaceMediaData(
+ targetId = KEY_MEDIA_SMARTSPACE,
+ isActive = true,
+ isValid = true,
+ packageName = PACKAGE_NAME,
+ cardAction = mediaSmartspaceBaseAction,
+ recommendations = listOf(mediaRecommendationItem),
+ dismissIntent = DISMISS_INTENT,
+ backgroundColor = 0,
+ headphoneConnectionTimeMillis = 1234L,
+ instanceId = InstanceId.fakeInstanceId(instanceId))),
eq(false))
}
@@ -464,12 +477,18 @@ class MediaDataManagerTest : SysuiTestCase() {
fun testOnSmartspaceMediaDataLoaded_hasNewInvalidMediaTarget_callsListener() {
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf())
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+ verify(logger).getNewInstanceId()
+ val instanceId = instanceIdSequence.lastInstanceId
+
verify(listener).onSmartspaceMediaDataLoaded(
eq(KEY_MEDIA_SMARTSPACE),
- eq(EMPTY_SMARTSPACE_MEDIA_DATA
- .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true,
- isValid = false, dismissIntent = DISMISS_INTENT,
- headphoneConnectionTimeMillis = 1234L)),
+ eq(EMPTY_SMARTSPACE_MEDIA_DATA.copy(
+ targetId = KEY_MEDIA_SMARTSPACE,
+ isActive = true,
+ isValid = false,
+ dismissIntent = DISMISS_INTENT,
+ headphoneConnectionTimeMillis = 1234L,
+ instanceId = InstanceId.fakeInstanceId(instanceId))),
eq(false))
}
@@ -484,18 +503,25 @@ class MediaDataManagerTest : SysuiTestCase() {
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf())
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+ verify(logger).getNewInstanceId()
+ val instanceId = instanceIdSequence.lastInstanceId
verify(listener).onSmartspaceMediaDataLoaded(
eq(KEY_MEDIA_SMARTSPACE),
- eq(EMPTY_SMARTSPACE_MEDIA_DATA
- .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true,
- isValid = false, dismissIntent = null, headphoneConnectionTimeMillis = 1234L)),
+ eq(EMPTY_SMARTSPACE_MEDIA_DATA.copy(
+ targetId = KEY_MEDIA_SMARTSPACE,
+ isActive = true,
+ isValid = false,
+ dismissIntent = null,
+ headphoneConnectionTimeMillis = 1234L,
+ instanceId = InstanceId.fakeInstanceId(instanceId))),
eq(false))
}
@Test
fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_notCallsListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf())
+ verify(logger, never()).getNewInstanceId()
verify(listener, never())
.onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean())
}
@@ -503,11 +529,14 @@ class MediaDataManagerTest : SysuiTestCase() {
@Test
fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_callsRemoveListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+ verify(logger).getNewInstanceId()
+
smartspaceMediaDataProvider.onTargetsAvailable(listOf())
foregroundExecutor.advanceClockToLast()
foregroundExecutor.runAllReady()
verify(listener).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(false))
+ verifyNoMoreInteractions(logger)
}
@Test