summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt103
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt97
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt58
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt54
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt2
27 files changed, 590 insertions, 125 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index f43fa5048298..24672ebe6134 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -183,7 +183,10 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE, true)
)
underTest.addSelectedUserMediaEntry(playingData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId))
+ underTest.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(playingInstanceId),
+ false
+ )
verify(smartspaceLogger)
.logSmartspaceCardReceived(
@@ -193,7 +196,10 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
)
underTest.addSelectedUserMediaEntry(remoteData)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(remoteInstanceId))
+ underTest.addMediaDataLoadingState(
+ MediaDataLoadingModel.Loaded(remoteInstanceId),
+ false
+ )
verify(smartspaceLogger)
.logSmartspaceCardReceived(
@@ -442,7 +448,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
reset(smartspaceLogger)
underTest.addSelectedUserMediaEntry(data)
- underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId), false)
verify(smartspaceLogger)
.logSmartspaceCardReceived(data.smartspaceId, data.appUid, cardinality = 2)
@@ -451,7 +457,8 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
underTest.addSelectedUserMediaEntry(data)
underTest.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(instanceId, receivedSmartspaceCardLatency = 123)
+ MediaDataLoadingModel.Loaded(instanceId, receivedSmartspaceCardLatency = 123),
+ true
)
verify(smartspaceLogger)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index c62195fafd8c..414974cc2941 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.media.controls.domain.interactor
import android.R
import android.graphics.drawable.Icon
+import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
@@ -38,12 +39,19 @@ import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
+import com.android.systemui.media.controls.util.SmallHash
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.reset
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -52,7 +60,11 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val mediaFilterRepository: MediaFilterRepository = kosmos.mediaFilterRepository
+ private val mediaFilterRepository: MediaFilterRepository =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaFilterRepository
+ }
private val mediaRecommendationsInteractor: MediaRecommendationsInteractor =
kosmos.mediaRecommendationsInteractor
val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
@@ -63,6 +75,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
packageName = PACKAGE_NAME,
recommendations = MediaTestHelper.getValidRecommendationList(icon),
)
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
private val underTest: MediaCarouselInteractor = kosmos.mediaCarouselInteractor
@@ -153,6 +166,18 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
MediaCommonModel.MediaControl(mediaLoadingModel, true)
)
.inOrder()
+
+ underTest.logSmartspaceSeenCard(0, 1, false)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ SmallHash.hash(mediaRecommendation.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ 2,
+ true
+ )
}
@Test
@@ -239,7 +264,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
.inOrder()
mediaFilterRepository.addSelectedUserMediaEntry(data.copy(isPlaying = true))
- mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+ mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
assertThat(currentMedia)
.containsExactly(
@@ -249,9 +274,83 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
.inOrder()
}
+ @Test
+ fun loadMediaAndRecommendation_logSmartspaceSeenCard() {
+ val instanceId = InstanceId.fakeInstanceId(123)
+ val data =
+ MediaData(
+ active = true,
+ instanceId = instanceId,
+ packageName = PACKAGE_NAME,
+ notificationKey = KEY
+ )
+ val smartspaceLoadingModel = SmartspaceMediaLoadingModel.Loaded(KEY_MEDIA_SMARTSPACE)
+ val mediaLoadingModel = MediaDataLoadingModel.Loaded(instanceId)
+
+ mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
+ underTest.logSmartspaceSeenCard(0, 1, false)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ data.smartspaceId,
+ data.appUid,
+ surface = SURFACE,
+ 1
+ )
+
+ reset(smartspaceLogger)
+ mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
+ underTest.logSmartspaceSeenCard(0, 1, true)
+
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ data.smartspaceId,
+ data.appUid,
+ surface = SURFACE,
+ 2
+ )
+
+ reset(smartspaceLogger)
+ mediaFilterRepository.setRecommendation(mediaRecommendation)
+ mediaFilterRepository.setRecommendationsLoadingState(smartspaceLoadingModel)
+ underTest.logSmartspaceSeenCard(1, 1, true)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ SmallHash.hash(mediaRecommendation.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ 2,
+ true,
+ rank = 1
+ )
+
+ reset(smartspaceLogger)
+ mediaFilterRepository.addSelectedUserMediaEntry(data)
+ mediaFilterRepository.addMediaDataLoadingState(
+ mediaLoadingModel.copy(receivedSmartspaceCardLatency = 1)
+ )
+ underTest.logSmartspaceSeenCard(0, 1, true)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_SEEN_EVENT,
+ data.smartspaceId,
+ data.appUid,
+ surface = SURFACE,
+ 2
+ )
+ }
+
companion object {
private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
private const val PACKAGE_NAME = "com.android.example"
private const val KEY = "key"
+ private const val SURFACE = 4
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
index 856c3fe19d73..d594f3a2f932 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.media.controls.domain.interactor
+import android.R
import android.app.PendingIntent
+import android.graphics.drawable.Icon
import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -30,6 +32,7 @@ import com.android.systemui.bluetooth.mockBroadcastDialogController
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.MediaTestHelper
import com.android.systemui.media.controls.data.repository.mediaDataRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
@@ -38,7 +41,12 @@ import com.android.systemui.media.controls.domain.pipeline.interactor.mediaContr
import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.mediaInstanceId
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.media.mediaOutputDialogManager
import com.android.systemui.mockActivityIntentHelper
import com.android.systemui.plugins.activityStarter
@@ -49,6 +57,8 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
@@ -63,11 +73,23 @@ class MediaControlInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
+ private val mediaDataFilter: MediaDataFilterImpl =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaDataFilter
+ }
private val activityStarter = kosmos.activityStarter
private val keyguardStateController = kosmos.keyguardStateController
private val instanceId: InstanceId = kosmos.mediaInstanceId
private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
+ private val icon = Icon.createWithResource(context, R.drawable.ic_media_play)
+ private val mediaRecommendation =
+ SmartspaceMediaData(
+ targetId = KEY_MEDIA_SMARTSPACE,
+ isActive = true,
+ recommendations = MediaTestHelper.getValidRecommendationList(icon),
+ )
private val underTest: MediaControlInteractor =
with(kosmos) {
@@ -124,13 +146,15 @@ class MediaControlInteractorTest : SysuiTestCase() {
val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
val expandable = mock<Expandable>()
- underTest.startClickIntent(expandable, clickIntent)
+ underTest.startClickIntent(expandable, clickIntent, SMARTSPACE_CARD_CLICK_EVENT, 1)
verify(clickIntent).send(any<Bundle>())
}
@Test
fun startClickIntent_hideOverLockscreen() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
whenever(keyguardStateController.isShowing).thenReturn(false)
val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
@@ -138,8 +162,20 @@ class MediaControlInteractorTest : SysuiTestCase() {
val activityController = mock<ActivityTransitionAnimator.Controller>()
whenever(expandable.activityTransitionController(any())).thenReturn(activityController)
- underTest.startClickIntent(expandable, clickIntent)
-
+ val mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, mediaRecommendation, true)
+ mediaDataFilter.onMediaDataLoaded(KEY, null, mediaData)
+ underTest.startClickIntent(expandable, clickIntent, SMARTSPACE_CARD_CLICK_EVENT, 1)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_CLICK_EVENT,
+ mediaData.smartspaceId,
+ mediaData.appUid,
+ surface = SURFACE,
+ cardinality = 2,
+ rank = 1
+ )
verify(activityStarter)
.postStartActivityDismissingKeyguard(eq(clickIntent), eq(activityController))
}
@@ -217,17 +253,62 @@ class MediaControlInteractorTest : SysuiTestCase() {
}
@Test
- fun removeMediaControl() {
+ fun removeMediaControl_noRecommendation() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+ val listener = mock<MediaDataProcessor.Listener>()
+ kosmos.mediaDataProcessor.addInternalListener(listener)
+
+ val mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+ kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData)
+ kosmos.mediaDataFilter.onMediaDataLoaded(KEY, null, mediaData)
+
+ underTest.removeMediaControl(null, instanceId, 0L, SMARTSPACE_CARD_DISMISS_EVENT, 1)
+ kosmos.fakeExecutor.advanceClockToNext()
+ kosmos.fakeExecutor.runAllReady()
+
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardUIEvent(
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean()
+ )
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
+ }
+
+ @Test
+ fun removeMediaControl_recommendationsExist() {
+ whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+ whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
val listener = mock<MediaDataProcessor.Listener>()
kosmos.mediaDataProcessor.addInternalListener(listener)
- var mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+ val mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, mediaRecommendation, true)
+ mediaDataFilter.onMediaDataLoaded(KEY, null, mediaData)
- underTest.removeMediaControl(null, instanceId, 0L)
+ underTest.removeMediaControl(null, instanceId, 0L, SMARTSPACE_CARD_DISMISS_EVENT, 1)
kosmos.fakeExecutor.advanceClockToNext()
kosmos.fakeExecutor.runAllReady()
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ mediaData.smartspaceId,
+ mediaData.appUid,
+ surface = SURFACE,
+ cardinality = 2,
+ rank = 1
+ )
verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
}
@@ -238,5 +319,7 @@ class MediaControlInteractorTest : SysuiTestCase() {
private const val APP_NAME = "app"
private const val ARTIST = "artist"
private const val ARTIST_2 = "artist2"
+ private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
+ private const val SURFACE = 4
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
index 9656511817dc..8af7e1dbe59b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaRecommendationsInteractorTest.kt
@@ -21,6 +21,7 @@ import android.content.ComponentName
import android.content.Intent
import android.content.applicationContext
import android.graphics.drawable.Icon
+import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -40,10 +41,14 @@ import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
import com.android.systemui.media.controls.shared.model.MediaRecModel
import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.util.SmallHash
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.plugins.activityStarter
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -53,6 +58,7 @@ import org.junit.runner.RunWith
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.kotlin.eq
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -62,7 +68,11 @@ class MediaRecommendationsInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos().apply { applicationContext = spyContext }
private val testScope = kosmos.testScope
- private val mediaDataFilter: MediaDataFilterImpl = kosmos.mediaDataFilter
+ private val mediaDataFilter: MediaDataFilterImpl =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaDataFilter
+ }
private val activityStarter = kosmos.activityStarter
private val icon: Icon = Icon.createWithResource(context, R.drawable.ic_media_play)
private val smartspaceMediaData: SmartspaceMediaData =
@@ -72,6 +82,7 @@ class MediaRecommendationsInteractorTest : SysuiTestCase() {
packageName = PACKAGE_NAME,
recommendations = MediaTestHelper.getValidRecommendationList(icon),
)
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
private val underTest: MediaRecommendationsInteractor =
with(kosmos) {
@@ -138,8 +149,24 @@ class MediaRecommendationsInteractorTest : SysuiTestCase() {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- underTest.removeMediaRecommendations(KEY_MEDIA_SMARTSPACE, intent, 0)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
+ underTest.removeMediaRecommendations(
+ KEY_MEDIA_SMARTSPACE,
+ intent,
+ 0,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ 1
+ )
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ SmallHash.hash(smartspaceMediaData.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ cardinality = 1,
+ isRecommendationCard = true,
+ )
verify(kosmos.mockBroadcastSender).sendBroadcast(eq(intent))
}
@@ -151,7 +178,13 @@ class MediaRecommendationsInteractorTest : SysuiTestCase() {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.component = ComponentName(PACKAGE_NAME, EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME)
- underTest.removeMediaRecommendations(KEY_MEDIA_SMARTSPACE, intent, 0)
+ underTest.removeMediaRecommendations(
+ KEY_MEDIA_SMARTSPACE,
+ intent,
+ 0,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ 1
+ )
verify(spyContext).startActivity(eq(intent))
}
@@ -171,13 +204,26 @@ class MediaRecommendationsInteractorTest : SysuiTestCase() {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- underTest.startClickIntent(expandable, intent)
-
+ mediaDataFilter.onSmartspaceMediaDataLoaded(KEY_MEDIA_SMARTSPACE, smartspaceMediaData)
+ underTest.startClickIntent(expandable, intent, SMARTSPACE_CARD_CLICK_EVENT, 1, 2, 3)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ SMARTSPACE_CARD_CLICK_EVENT,
+ SmallHash.hash(smartspaceMediaData.targetId),
+ Process.INVALID_UID,
+ surface = SURFACE,
+ cardinality = 1,
+ isRecommendationCard = true,
+ interactedSubcardRank = 2,
+ interactedSubcardCardinality = 3
+ )
verify(spyContext).startActivity(eq(intent))
}
companion object {
private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
private const val PACKAGE_NAME = "com.example.app"
+ private const val SURFACE = 4
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
index 71685a4354bf..005424ba599e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
@@ -47,7 +47,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> assertThat(commonViewModel).isEqualTo(mediaControl) },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -66,7 +66,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -85,7 +85,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel -> assertThat(commonViewModel).isNotEqualTo(mediaControl) },
+ { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaControl) },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -104,7 +104,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel -> assertThat(commonViewModel).isNotEqualTo(mediaRecs) },
+ { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaRecs) },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -124,7 +124,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaControl1) },
)
@@ -145,7 +145,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ fail("Unexpected to remove $it") },
{ commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
)
@@ -164,7 +164,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaControl) },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
@@ -183,7 +183,7 @@ class MediaDiffUtilTest : SysuiTestCase() {
oldList,
newList,
{ commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { fail("Unexpected to update $it") },
+ { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
{ commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaRecs) },
{ commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
index e8a23347c3c3..341b8d87eeef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
@@ -28,6 +28,7 @@ import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_SEEN_EVENT
import com.android.systemui.media.controls.util.SmallHash
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.time.SystemClock
@@ -137,10 +138,13 @@ constructor(
return mediaData
}
- fun addSelectedUserMediaEntry(data: MediaData) {
+ /** @return whether the added media data already exists. */
+ fun addSelectedUserMediaEntry(data: MediaData): Boolean {
val entries = LinkedHashMap<InstanceId, MediaData>(_selectedUserEntries.value)
+ val update = _selectedUserEntries.value.containsKey(data.instanceId)
entries[data.instanceId] = data
_selectedUserEntries.value = entries
+ return update
}
/**
@@ -184,7 +188,10 @@ constructor(
_reactivatedId.value = instanceId
}
- fun addMediaDataLoadingState(mediaDataLoadingModel: MediaDataLoadingModel) {
+ fun addMediaDataLoadingState(
+ mediaDataLoadingModel: MediaDataLoadingModel,
+ isUpdate: Boolean = true
+ ) {
val sortedMap = TreeMap<MediaSortKeyModel, MediaCommonModel>(comparator)
sortedMap.putAll(
sortedMedia.filter { (_, commonModel) ->
@@ -212,15 +219,10 @@ constructor(
MediaCommonModel.MediaControl(
mediaDataLoadingModel,
canBeRemoved(it),
- isMediaFromRec(it)
+ isMediaFromRec(it),
+ if (isUpdate) systemClock.currentTimeMillis() else 0,
)
sortedMap[sortKey] = newCommonModel
- val isUpdate =
- sortedMedia.values.any { commonModel ->
- commonModel is MediaCommonModel.MediaControl &&
- commonModel.mediaLoadedModel.instanceId ==
- mediaDataLoadingModel.instanceId
- }
// On Addition or tapping on recommendations, we should show the new order of media.
if (mediaFromRecPackageName == it.packageName) {
@@ -359,14 +361,51 @@ constructor(
return _selectedUserEntries.value.entries.isNotEmpty()
}
+ fun hasActiveMediaOrRecommendation(): Boolean {
+ return _selectedUserEntries.value.any { it.value.active } ||
+ (isRecommendationActive() &&
+ (_smartspaceMediaData.value.isValid() || _reactivatedId.value != null))
+ }
+
fun isRecommendationActive(): Boolean {
return _smartspaceMediaData.value.isActive
}
+ /** Log visible card given [visibleIndex]. */
+ fun logSmartspaceCardSeen(surface: Int, visibleIndex: Int, isMediaCardUpdate: Boolean) {
+ if (_currentMedia.value.size <= visibleIndex) return
+
+ when (val mediaCommonModel = _currentMedia.value[visibleIndex]) {
+ is MediaCommonModel.MediaControl -> {
+ if (
+ !isMediaCardUpdate ||
+ mediaCommonModel.mediaLoadedModel.receivedSmartspaceCardLatency != 0
+ ) {
+ logSmartspaceMediaCardUserEvent(
+ mediaCommonModel.mediaLoadedModel.instanceId,
+ visibleIndex,
+ SMARTSPACE_CARD_SEEN_EVENT,
+ surface,
+ mediaCommonModel.mediaLoadedModel.isSsReactivated,
+ )
+ }
+ }
+ is MediaCommonModel.MediaRecommendations -> {
+ if (isRecommendationActive()) {
+ logSmarspaceRecommendationCardUserEvent(
+ SMARTSPACE_CARD_SEEN_EVENT,
+ surface,
+ visibleIndex
+ )
+ }
+ }
+ }
+ }
+
/** Log user event on media card if smartspace logging is enabled. */
fun logSmartspaceCardUserEvent(
eventId: Int,
- location: Int,
+ surface: Int,
interactedSubCardRank: Int = 0,
interactedSubCardCardinality: Int = 0,
instanceId: InstanceId? = null,
@@ -381,7 +420,7 @@ constructor(
instanceId,
index,
eventId,
- location,
+ surface,
mediaCommonModel.mediaLoadedModel.isSsReactivated,
interactedSubCardRank,
interactedSubCardCardinality
@@ -395,7 +434,7 @@ constructor(
if (isSmartspaceLoggingEnabled(mediaCommonModel, index)) {
logSmarspaceRecommendationCardUserEvent(
eventId,
- location,
+ surface,
index,
interactedSubCardRank,
interactedSubCardCardinality
@@ -409,7 +448,7 @@ constructor(
}
/** Log media and recommendation cards dismissal if smartspace logging is enabled for each. */
- fun logSmartspaceCardsOnSwipeToDismiss(location: Int) {
+ fun logSmartspaceCardsOnSwipeToDismiss(surface: Int) {
_currentMedia.value.forEachIndexed { index, mediaCommonModel ->
if (isSmartspaceLoggingEnabled(mediaCommonModel, index)) {
when (mediaCommonModel) {
@@ -418,14 +457,14 @@ constructor(
mediaCommonModel.mediaLoadedModel.instanceId,
index,
SMARTSPACE_CARD_DISMISS_EVENT,
- location,
+ surface,
mediaCommonModel.mediaLoadedModel.isSsReactivated,
isSwipeToDismiss = true
)
is MediaCommonModel.MediaRecommendations ->
logSmarspaceRecommendationCardUserEvent(
SMARTSPACE_CARD_DISMISS_EVENT,
- location,
+ surface,
index,
isSwipeToDismiss = true
)
@@ -470,7 +509,7 @@ constructor(
instanceId: InstanceId,
index: Int,
eventId: Int,
- location: Int,
+ surface: Int,
isReactivated: Boolean,
interactedSubCardRank: Int = 0,
interactedSubCardCardinality: Int = 0,
@@ -481,7 +520,7 @@ constructor(
eventId,
it.smartspaceId,
it.appUid,
- location,
+ surface,
_currentMedia.value.size,
isSsReactivated = isReactivated,
interactedSubcardRank = interactedSubCardRank,
@@ -494,7 +533,7 @@ constructor(
private fun logSmarspaceRecommendationCardUserEvent(
eventId: Int,
- location: Int,
+ surface: Int,
index: Int,
interactedSubCardRank: Int = 0,
interactedSubCardCardinality: Int = 0,
@@ -504,7 +543,7 @@ constructor(
eventId,
SmallHash.hash(_smartspaceMediaData.value.targetId),
_smartspaceMediaData.value.getUid(applicationContext),
- location,
+ surface,
_currentMedia.value.size,
isRecommendationCard = true,
interactedSubcardRank = interactedSubCardRank,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index 31bd4fb78ffa..803e7efa7f60 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -116,11 +116,12 @@ constructor(
return
}
- mediaFilterRepository.addSelectedUserMediaEntry(data)
+ val isUpdate = mediaFilterRepository.addSelectedUserMediaEntry(data)
mediaLoadingLogger.logMediaLoaded(data.instanceId, data.active, "loading media")
mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(data.instanceId)
+ MediaDataLoadingModel.Loaded(data.instanceId),
+ isUpdate
)
// Notify listeners
@@ -323,9 +324,10 @@ constructor(
mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
if (lockscreenUserManager.isCurrentProfile(data.userId)) {
- mediaFilterRepository.addSelectedUserMediaEntry(data)
+ val isUpdate = mediaFilterRepository.addSelectedUserMediaEntry(data)
mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(data.instanceId)
+ MediaDataLoadingModel.Loaded(data.instanceId),
+ isUpdate
)
mediaLoadingLogger.logMediaLoaded(
data.instanceId,
@@ -338,8 +340,9 @@ constructor(
}
/** Invoked when the user has dismissed the media carousel */
- fun onSwipeToDismiss() {
+ fun onSwipeToDismiss(surface: Int) {
if (DEBUG) Log.d(TAG, "Media carousel swiped away")
+ mediaFilterRepository.logSmartspaceCardsOnSwipeToDismiss(surface)
val mediaEntries = mediaFilterRepository.allUserEntries.value.entries
mediaEntries.forEach { (key, data) ->
if (mediaFilterRepository.selectedUserEntries.value.containsKey(data.instanceId)) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index 0630cbd3f3be..9d7160cbaffc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -36,6 +36,7 @@ import com.android.systemui.media.controls.domain.pipeline.MediaTimeoutListener
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
import com.android.systemui.media.controls.shared.model.MediaCommonModel
import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import java.io.PrintWriter
import javax.inject.Inject
@@ -204,11 +205,14 @@ constructor(
mediaDataProcessor.setMediaResumptionEnabled(isEnabled)
}
- override fun onSwipeToDismiss() {
- mediaDataFilter.onSwipeToDismiss()
+ override fun onSwipeToDismiss() = unsupported
+
+ fun onSwipeToDismiss(location: Int) {
+ mediaDataFilter.onSwipeToDismiss(MediaSmartspaceLogger.getSurface(location))
}
- override fun hasActiveMediaOrRecommendation() = hasActiveMediaOrRecommendation.value
+ override fun hasActiveMediaOrRecommendation() =
+ mediaFilterRepository.hasActiveMediaOrRecommendation()
override fun hasAnyMediaOrRecommendation() = hasAnyMediaOrRecommendation.value
@@ -222,6 +226,14 @@ constructor(
mediaFilterRepository.setOrderedMedia()
}
+ fun logSmartspaceSeenCard(visibleIndex: Int, location: Int, isMediaCardUpdate: Boolean) {
+ mediaFilterRepository.logSmartspaceCardSeen(
+ MediaSmartspaceLogger.getSurface(location),
+ visibleIndex,
+ isMediaCardUpdate
+ )
+ }
+
/** Add a listener for internal events. */
private fun addInternalListener(listener: MediaDataManager.Listener) =
mediaDataProcessor.addInternalListener(listener)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index 3f75938a91ea..245f6f8bf2f6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -19,7 +19,6 @@ package com.android.systemui.media.controls.domain.pipeline.interactor
import android.app.ActivityOptions
import android.app.BroadcastOptions
import android.app.PendingIntent
-import android.content.Context
import android.content.Intent
import android.media.session.MediaSession
import android.provider.Settings
@@ -31,11 +30,11 @@ import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.bluetooth.BroadcastDialogController
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
import com.android.systemui.media.controls.shared.model.MediaControlModel
import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.media.dialog.MediaOutputDialogManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -50,9 +49,8 @@ import kotlinx.coroutines.flow.map
class MediaControlInteractor
@AssistedInject
constructor(
- @Application applicationContext: Context,
@Assisted private val instanceId: InstanceId,
- repository: MediaFilterRepository,
+ private val repository: MediaFilterRepository,
private val mediaDataProcessor: MediaDataProcessor,
private val keyguardStateController: KeyguardStateController,
private val activityStarter: ActivityStarter,
@@ -72,8 +70,11 @@ constructor(
fun removeMediaControl(
token: MediaSession.Token?,
instanceId: InstanceId,
- delayMs: Long
+ delayMs: Long,
+ eventId: Int,
+ location: Int
): Boolean {
+ logSmartspaceUserEvent(eventId, location)
val dismissed =
mediaDataProcessor.dismissMediaData(instanceId, delayMs, userInitiated = true)
if (!dismissed) {
@@ -114,7 +115,13 @@ constructor(
activityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */ true)
}
- fun startClickIntent(expandable: Expandable, clickIntent: PendingIntent) {
+ fun startClickIntent(
+ expandable: Expandable,
+ clickIntent: PendingIntent,
+ eventId: Int,
+ location: Int
+ ) {
+ logSmartspaceUserEvent(eventId, location)
if (!launchOverLockscreen(clickIntent)) {
activityStarter.postStartActivityDismissingKeyguard(
clickIntent,
@@ -176,6 +183,14 @@ constructor(
)
}
+ fun logSmartspaceUserEvent(eventId: Int, location: Int) {
+ repository.logSmartspaceCardUserEvent(
+ eventId,
+ MediaSmartspaceLogger.getSurface(location),
+ instanceId = instanceId
+ )
+ }
+
private fun Expandable.dialogController(): DialogTransitionAnimator.Controller? {
return dialogTransitionController(
cuj =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
index dd6b2646afd1..c3a36b258842 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaRecommendationsInteractor.kt
@@ -31,6 +31,7 @@ import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
import com.android.systemui.media.controls.shared.model.MediaRecModel
import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.plugins.ActivityStarter
import java.net.URISyntaxException
import javax.inject.Inject
@@ -67,7 +68,14 @@ constructor(
val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange
- fun removeMediaRecommendations(key: String, dismissIntent: Intent?, delayMs: Long) {
+ fun removeMediaRecommendations(
+ key: String,
+ dismissIntent: Intent?,
+ delayMs: Long,
+ eventId: Int,
+ location: Int
+ ) {
+ logSmartspaceCardUserEvent(eventId, location)
mediaDataProcessor.dismissSmartspaceRecommendation(key, delayMs)
if (dismissIntent == null) {
Log.w(TAG, "Cannot create dismiss action click action: extras missing dismiss_intent.")
@@ -87,7 +95,25 @@ constructor(
activityStarter.startActivity(SETTINGS_INTENT, /* dismissShade= */ true)
}
- fun startClickIntent(expandable: Expandable, intent: Intent) {
+ fun startClickIntent(
+ expandable: Expandable,
+ intent: Intent,
+ eventId: Int,
+ location: Int,
+ interactedSubCardRank: Int,
+ interactedSubCardCardinality: Int
+ ) {
+ if (interactedSubCardRank == -1) {
+ logSmartspaceCardUserEvent(eventId, MediaSmartspaceLogger.getSurface(location))
+ } else {
+ repository.logSmartspaceCardUserEvent(
+ eventId,
+ MediaSmartspaceLogger.getSurface(location),
+ interactedSubCardRank = interactedSubCardRank,
+ interactedSubCardCardinality = interactedSubCardCardinality,
+ isRec = true
+ )
+ }
if (shouldActivityOpenInForeground(intent)) {
// Request to unlock the device if the activity needs to be opened in foreground.
activityStarter.postStartActivityDismissingKeyguard(
@@ -103,6 +129,14 @@ constructor(
}
}
+ private fun logSmartspaceCardUserEvent(eventId: Int, location: Int) {
+ repository.logSmartspaceCardUserEvent(
+ eventId,
+ MediaSmartspaceLogger.getSurface(location),
+ isRec = true
+ )
+ }
+
/** Returns if the action will open the activity in foreground. */
private fun shouldActivityOpenInForeground(intent: Intent): Boolean {
val intentString = intent.extras?.getString(EXTRAS_SMARTSPACE_INTENT) ?: return false
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
index 56cc618eb61c..3d5d47b30860 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaCommonModel.kt
@@ -22,6 +22,7 @@ sealed class MediaCommonModel {
val mediaLoadedModel: MediaDataLoadingModel.Loaded,
val canBeRemoved: Boolean = false,
val isMediaFromRec: Boolean = false,
+ val updateTime: Long = 0L,
) : MediaCommonModel()
data class MediaRecommendations(val recsLoadingModel: SmartspaceMediaLoadingModel) :
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 72fb218865b2..62759a4843d9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -109,6 +109,10 @@ object MediaControlViewBinder {
mainDispatcher: CoroutineDispatcher,
mediaFlags: MediaFlags,
) {
+ // Set up media control location and its listener.
+ viewModel.onLocationChanged(viewController.currentEndLocation)
+ viewController.locationChangeListener = viewModel.onLocationChanged
+
with(viewHolder) {
// AlbumView uses a hardware layer so that clipping of the foreground is handled with
// clipping the album art. Otherwise album art shows through at the edges.
@@ -221,7 +225,7 @@ object MediaControlViewBinder {
dismiss.isEnabled = model.isDismissEnabled
dismiss.setOnClickListener {
if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- model.onDismissClicked.invoke()
+ model.onDismissClicked()
}
}
cancelText.background = model.cancelTextBackground
@@ -349,7 +353,7 @@ object MediaControlViewBinder {
if (actionViewModel.isEnabled) {
button.setOnClickListener {
if (!falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)) {
- actionViewModel.onClicked.invoke(it.id)
+ actionViewModel.onClicked(it.id)
viewController.multiRippleController.play(
createTouchRippleAnimation(
@@ -469,8 +473,7 @@ object MediaControlViewBinder {
transitionDrawable.startTransition(
if (viewModel.shouldAddGradient) 333 else 80
)
- }
- ?: albumView.setImageDrawable(artwork)
+ } ?: albumView.setImageDrawable(artwork)
}
viewController.isArtworkBound = viewModel.shouldAddGradient
viewController.prevArtwork = artwork
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
index bd4d43531339..5e8a879adbb4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaRecommendationsViewBinder.kt
@@ -71,14 +71,18 @@ object MediaRecommendationsViewBinder {
fun bindRecsCard(
viewHolder: RecommendationViewHolder,
viewModel: MediaRecsCardViewModel,
- mediaViewController: MediaViewController,
+ viewController: MediaViewController,
falsingManager: FalsingManager,
) {
+ // Set up media control location and its listener.
+ viewModel.onLocationChanged(viewController.currentEndLocation)
+ viewController.locationChangeListener = viewModel.onLocationChanged
+
// Bind main card.
viewHolder.cardTitle.setTextColor(viewModel.cardTitleColor)
viewHolder.recommendations.backgroundTintList = ColorStateList.valueOf(viewModel.cardColor)
viewHolder.recommendations.contentDescription =
- viewModel.contentDescription.invoke(mediaViewController.isGutsVisible)
+ viewModel.contentDescription.invoke(viewController.isGutsVisible)
viewHolder.recommendations.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
@@ -88,21 +92,21 @@ object MediaRecommendationsViewBinder {
viewHolder.recommendations.setOnLongClickListener {
if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY))
return@setOnLongClickListener true
- if (!mediaViewController.isGutsVisible) {
- openGuts(viewHolder, viewModel, mediaViewController)
+ if (!viewController.isGutsVisible) {
+ openGuts(viewHolder, viewModel, viewController)
} else {
- closeGuts(viewHolder, viewModel, mediaViewController)
+ closeGuts(viewHolder, viewModel, viewController)
}
return@setOnLongClickListener true
}
// Bind all recommendations.
bindRecommendationsList(viewHolder, viewModel.mediaRecs, falsingManager)
- updateRecommendationsVisibility(mediaViewController, viewHolder.recommendations)
+ updateRecommendationsVisibility(viewController, viewHolder.recommendations)
// Set visibility of recommendations.
- val expandedSet: ConstraintSet = mediaViewController.expandedLayout
- val collapsedSet: ConstraintSet = mediaViewController.collapsedLayout
+ val expandedSet: ConstraintSet = viewController.expandedLayout
+ val collapsedSet: ConstraintSet = viewController.collapsedLayout
viewHolder.mediaTitles.forEach {
setVisibleAndAlpha(expandedSet, it.id, viewModel.areTitlesVisible)
setVisibleAndAlpha(collapsedSet, it.id, viewModel.areTitlesVisible)
@@ -112,15 +116,15 @@ object MediaRecommendationsViewBinder {
setVisibleAndAlpha(collapsedSet, it.id, viewModel.areSubtitlesVisible)
}
- bindRecommendationsGuts(viewHolder, viewModel, mediaViewController, falsingManager)
+ bindRecommendationsGuts(viewHolder, viewModel, viewController, falsingManager)
- mediaViewController.refreshState()
+ viewController.refreshState()
}
private fun bindRecommendationsGuts(
viewHolder: RecommendationViewHolder,
viewModel: MediaRecsCardViewModel,
- mediaViewController: MediaViewController,
+ viewController: MediaViewController,
falsingManager: FalsingManager,
) {
val gutsViewHolder = viewHolder.gutsViewHolder
@@ -131,14 +135,14 @@ object MediaRecommendationsViewBinder {
gutsViewHolder.dismiss.isEnabled = true
gutsViewHolder.dismiss.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
- closeGuts(viewHolder, viewModel, mediaViewController)
- gutsViewModel.onDismissClicked.invoke()
+ closeGuts(viewHolder, viewModel, viewController)
+ gutsViewModel.onDismissClicked()
}
gutsViewHolder.cancelText.background = gutsViewModel.cancelTextBackground
gutsViewHolder.cancel.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- closeGuts(viewHolder, viewModel, mediaViewController)
+ closeGuts(viewHolder, viewModel, viewController)
}
}
@@ -173,7 +177,7 @@ object MediaRecommendationsViewBinder {
val mediaCoverContainer = viewHolder.mediaCoverContainers[index]
mediaCoverContainer.setOnClickListener {
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return@setOnClickListener
- mediaRecViewModel.onClicked.invoke(Expandable.fromView(it), index)
+ mediaRecViewModel.onClicked(Expandable.fromView(it), index)
}
mediaCoverContainer.setOnLongClickListener {
if (falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY))
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 886cb7f85b82..125f97375fea 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -768,6 +768,7 @@ constructor(
mediaContent.addView(viewHolder.recommendations, position)
}
}
+ onAddOrUpdateVisibleToUserCard(position, isMediaCardUpdate = false)
viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded)
controllerByViewModel[commonViewModel] = viewController
updateViewControllerToState(viewController, noAnimation = true)
@@ -785,10 +786,14 @@ constructor(
commonViewModel.onAdded(commonViewModel)
}
- private fun onUpdated(commonViewModel: MediaCommonViewModel) {
+ private fun onUpdated(commonViewModel: MediaCommonViewModel, position: Int) {
commonViewModel.onUpdated(commonViewModel)
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
+ onAddOrUpdateVisibleToUserCard(
+ position,
+ commonViewModel is MediaCommonViewModel.MediaControl
+ )
}
private fun onRemoved(commonViewModel: MediaCommonViewModel) {
@@ -825,6 +830,20 @@ constructor(
mediaCarouselScrollHandler.onPlayersChanged()
}
+ private fun onAddOrUpdateVisibleToUserCard(position: Int, isMediaCardUpdate: Boolean) {
+ if (
+ mediaCarouselScrollHandler.visibleToUser &&
+ mediaCarouselScrollHandler.visibleMediaIndex == position
+ ) {
+ mediaCarouselViewModel.onCardVisibleToUser(
+ mediaCarouselScrollHandler.qsExpanded,
+ mediaCarouselScrollHandler.visibleMediaIndex,
+ currentEndLocation,
+ isMediaCardUpdate
+ )
+ }
+ }
+
private fun setNewViewModelsList(viewModels: List<MediaCommonViewModel>) {
commonViewModels.clear()
commonViewModels.addAll(viewModels)
@@ -1438,6 +1457,14 @@ constructor(
/** Log the user impression for media card at visibleMediaIndex. */
fun logSmartspaceImpression(qsExpanded: Boolean) {
+ if (SceneContainerFlag.isEnabled) {
+ mediaCarouselViewModel.onCardVisibleToUser(
+ qsExpanded,
+ mediaCarouselScrollHandler.visibleMediaIndex,
+ currentEndLocation
+ )
+ return
+ }
val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex
if (MediaPlayerData.players().size > visibleMediaIndex) {
val mediaControlPanel = MediaPlayerData.getMediaControlPanel(visibleMediaIndex)
@@ -1550,7 +1577,7 @@ constructor(
@VisibleForTesting
fun onSwipeToDismiss() {
if (mediaFlags.isSceneContainerEnabled()) {
- mediaCarouselViewModel.onSwipeToDismiss()
+ mediaCarouselViewModel.onSwipeToDismiss(currentEndLocation)
return
}
MediaPlayerData.players().forEachIndexed { index, it ->
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 9d0723211d4b..681bf390e3e9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -103,6 +103,7 @@ constructor(
lateinit var sizeChangedListener: () -> Unit
lateinit var configurationChangeListener: () -> Unit
lateinit var recsConfigurationChangeListener: (MediaViewController, TransitionLayout) -> Unit
+ var locationChangeListener: (Int) -> Unit = {}
private var firstRefresh: Boolean = true
@VisibleForTesting private var transitionLayout: TransitionLayout? = null
private val layoutController = TransitionLayoutController()
@@ -119,7 +120,15 @@ constructor(
* The ending location of the view where it ends when all animations and transitions have
* finished
*/
- @MediaLocation var currentEndLocation: Int = -1
+ @MediaLocation
+ var currentEndLocation: Int = -1
+ set(value) {
+ if (field != value) {
+ field = value
+ if (!mediaFlags.isSceneContainerEnabled()) return
+ locationChangeListener(value)
+ }
+ }
/** The starting location of the view where it starts for all animations and transitions */
@MediaLocation private var currentStartLocation: Int = -1
@@ -799,7 +808,7 @@ constructor(
fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) {
if (!mediaFlags.isSceneContainerEnabled()) return
seekBarViewModel.logSeek = onSeek
- onBindSeekBar.invoke(seekBarViewModel)
+ onBindSeekBar(seekBarViewModel)
}
fun setUpTurbulenceNoise() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
index 952b134c5e16..f28edd638b10 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
@@ -54,7 +54,8 @@ class MediaViewModelCallback(
oldItem is MediaCommonViewModel.MediaControl &&
newItem is MediaCommonViewModel.MediaControl
) {
- oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi
+ oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi &&
+ oldItem.updateTime == newItem.updateTime
} else if (
oldItem is MediaCommonViewModel.MediaRecommendations &&
newItem is MediaCommonViewModel.MediaRecommendations
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
index bd81e44d091c..709723fa9480 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
@@ -24,7 +24,7 @@ class MediaViewModelListUpdateCallback(
private val old: List<MediaCommonViewModel>,
private val new: List<MediaCommonViewModel>,
private val onAdded: (MediaCommonViewModel, Int) -> Unit,
- private val onUpdated: (MediaCommonViewModel) -> Unit,
+ private val onUpdated: (MediaCommonViewModel, Int) -> Unit,
private val onRemoved: (MediaCommonViewModel) -> Unit,
private val onMoved: (MediaCommonViewModel, Int, Int) -> Unit,
) : ListUpdateCallback {
@@ -47,7 +47,7 @@ class MediaViewModelListUpdateCallback(
override fun onChanged(position: Int, count: Int, payload: Any?) {
for (i in position until position + count) {
- onUpdated(new[i])
+ onUpdated(new[i], position)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index f0d8df511c73..c453a212a3cd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -98,9 +98,9 @@ constructor(
private var allowReorder = false
- fun onSwipeToDismiss() {
+ fun onSwipeToDismiss(location: Int) {
logger.logSwipeDismiss()
- interactor.onSwipeToDismiss()
+ interactor.onSwipeToDismiss(location)
}
fun onReorderingAllowed() {
@@ -108,12 +108,24 @@ constructor(
interactor.reorderMedia()
}
+ fun onCardVisibleToUser(
+ qsExpanded: Boolean,
+ visibleIndex: Int,
+ location: Int,
+ isUpdate: Boolean = false
+ ) {
+ // Skip logging if on LS or QQS, and there is no active media card
+ if (!qsExpanded && !interactor.hasActiveMediaOrRecommendation()) return
+ interactor.logSmartspaceSeenCard(visibleIndex, location, isUpdate)
+ }
+
private fun toViewModel(
commonModel: MediaCommonModel.MediaControl
): MediaCommonViewModel.MediaControl {
val instanceId = commonModel.mediaLoadedModel.instanceId
return mediaControlByInstanceId[instanceId]?.copy(
- immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi
+ immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi,
+ updateTime = commonModel.updateTime
)
?: MediaCommonViewModel.MediaControl(
instanceId = instanceId,
@@ -125,7 +137,8 @@ constructor(
mediaControlByInstanceId.remove(instanceId)
},
onUpdated = { onMediaControlAddedOrUpdated(it, commonModel) },
- isMediaFromRec = commonModel.isMediaFromRec
+ isMediaFromRec = commonModel.isMediaFromRec,
+ updateTime = commonModel.updateTime
)
.also { mediaControlByInstanceId[instanceId] = it }
}
@@ -175,7 +188,6 @@ constructor(
commonViewModel: MediaCommonViewModel,
commonModel: MediaCommonModel.MediaControl
) {
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_RECEIVED)
if (commonModel.canBeRemoved && !Utils.useMediaResumption(applicationContext)) {
// This media control is due for removal as it is now paused + timed out, and resumption
// setting is off.
@@ -196,8 +208,6 @@ constructor(
if (!mediaFlags.isPersistentSsCardEnabled()) {
commonViewModel.onRemoved(true)
}
- } else {
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_RECEIVED)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
index a96d75c9ed30..52cb173b39cb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
@@ -33,6 +33,7 @@ sealed class MediaCommonViewModel {
override val onRemoved: (Boolean) -> Unit,
override val onUpdated: (MediaCommonViewModel) -> Unit,
val isMediaFromRec: Boolean = false,
+ val updateTime: Long = 0,
) : MediaCommonViewModel()
data class MediaRecommendations(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 099991d7c671..64820e0d0ced 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -37,6 +37,8 @@ import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
@@ -72,6 +74,7 @@ class MediaControlViewModel(
private var isPlaying = false
private var isAnyButtonClicked = false
+ private var location = -1
private fun onDismissMediaData(
token: Token?,
@@ -80,7 +83,13 @@ class MediaControlViewModel(
instanceId: InstanceId
) {
logger.logLongPressDismiss(uid, packageName, instanceId)
- interactor.removeMediaControl(token, instanceId, MEDIA_PLAYER_ANIMATION_DELAY)
+ interactor.removeMediaControl(
+ token,
+ instanceId,
+ MEDIA_PLAYER_ANIMATION_DELAY,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ location
+ )
}
private suspend fun toViewModel(model: MediaControlModel): MediaPlayerViewModel? {
@@ -100,7 +109,7 @@ class MediaControlViewModel(
TAG,
Style.CONTENT
)
- ?: return null
+ ?: return null
val gutsViewModel = toGutsViewModel(model, scheme)
@@ -144,8 +153,12 @@ class MediaControlViewModel(
onClicked = { expandable ->
model.clickIntent?.let { clickIntent ->
logger.logTapContentView(model.uid, model.packageName, model.instanceId)
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
- interactor.startClickIntent(expandable, clickIntent)
+ interactor.startClickIntent(
+ expandable,
+ clickIntent,
+ SMARTSPACE_CARD_CLICK_EVENT,
+ location
+ )
}
},
onLongClicked = {
@@ -153,7 +166,7 @@ class MediaControlViewModel(
},
onSeek = {
logger.logSeek(model.uid, model.packageName, model.instanceId)
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
+ interactor.logSmartspaceUserEvent(SMARTSPACE_CARD_CLICK_EVENT, location)
},
onBindSeekbar = { seekBarViewModel ->
if (model.isResume && model.resumeProgress != null) {
@@ -163,7 +176,8 @@ class MediaControlViewModel(
seekBarViewModel.updateController(mediaController)
}
}
- }
+ },
+ onLocationChanged = { location = it }
)
}
@@ -179,8 +193,7 @@ class MediaControlViewModel(
it,
applicationContext.getString(R.string.broadcasting_description_is_broadcasting)
)
- }
- ?: false
+ } ?: false
val useDisabledAlpha =
if (showBroadcastButton) {
!isCurrentBroadcastApp
@@ -197,7 +210,8 @@ class MediaControlViewModel(
return MediaOutputSwitcherViewModel(
isTapEnabled = showBroadcastButton || !useDisabledAlpha,
deviceString = deviceString,
- deviceIcon = device?.icon?.let { Icon.Loaded(it, null) }
+ deviceIcon =
+ device?.icon?.let { Icon.Loaded(it, null) }
?: if (showBroadcastButton) {
Icon.Resource(R.drawable.settings_input_antenna, null)
} else {
@@ -364,7 +378,7 @@ class MediaControlViewModel(
action: Runnable
) {
logger.logTapAction(id, uid, packageName, instanceId)
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
+ interactor.logSmartspaceUserEvent(SMARTSPACE_CARD_CLICK_EVENT, location)
isAnyButtonClicked = true
action.run()
}
@@ -385,8 +399,7 @@ class MediaControlViewModel(
SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.stream().allMatch { id: Int ->
semanticActions.getActionById(id) != null
}
- }
- ?: false
+ } ?: false
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
index 433434129b96..96e7fc79c8eb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
@@ -42,4 +42,5 @@ data class MediaPlayerViewModel(
val onLongClicked: () -> Unit,
val onSeek: () -> Unit,
val onBindSeekbar: (SeekBarViewModel) -> Unit,
+ val onLocationChanged: (Int) -> Unit,
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
index 52c4bc54eab4..1fd9c4f014ee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecommendationsViewModel.kt
@@ -37,6 +37,7 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaRecommendationsInteractor
import com.android.systemui.media.controls.shared.model.MediaRecModel
import com.android.systemui.media.controls.shared.model.MediaRecommendationsModel
+import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
@@ -44,6 +45,8 @@ import com.android.systemui.media.controls.ui.animation.textSecondaryFromScheme
import com.android.systemui.media.controls.ui.controller.MediaViewController.Companion.GUTS_ANIMATION_DURATION
import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
import com.android.systemui.media.controls.util.MediaDataUtils
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_CLICK_EVENT
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger.Companion.SMARTSPACE_CARD_DISMISS_EVENT
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
@@ -78,6 +81,8 @@ constructor(
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
+ private var location = -1
+
/**
* Called whenever the recommendation has been expired or removed by the user. This method
* removes the recommendation card entirely from the carousel.
@@ -89,9 +94,14 @@ constructor(
dismissIntent: Intent?,
instanceId: InstanceId?
) {
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_DISMISS_EVENT).
logger.logLongPressDismiss(uid, packageName, instanceId)
- interactor.removeMediaRecommendations(key, dismissIntent, GUTS_DISMISS_DELAY_MS_DURATION)
+ interactor.removeMediaRecommendations(
+ key,
+ dismissIntent,
+ GUTS_DISMISS_DELAY_MS_DURATION,
+ SMARTSPACE_CARD_DISMISS_EVENT,
+ location
+ )
}
private fun onClicked(
@@ -111,12 +121,18 @@ constructor(
} else {
logger.logRecommendationItemTap(packageName, instanceId, index)
}
- // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT).
// set the package name of the player added by recommendation once the media is loaded.
interactor.switchToMediaControl(packageName)
- interactor.startClickIntent(expandable, intent)
+ interactor.startClickIntent(
+ expandable,
+ intent,
+ SMARTSPACE_CARD_CLICK_EVENT,
+ location,
+ index,
+ NUM_REQUIRED_RECOMMENDATIONS
+ )
}
private suspend fun toRecsViewModel(model: MediaRecommendationsModel): MediaRecsCardViewModel? {
@@ -212,6 +228,7 @@ constructor(
areTitlesVisible = areTitlesVisible,
areSubtitlesVisible = areSubtitlesVisible,
gutsMenu = toGutsViewModel(model, scheme),
+ onLocationChanged = { location = it }
)
}
@@ -259,8 +276,7 @@ constructor(
width,
height
)
- }
- ?: ColorDrawable(Color.TRANSPARENT)
+ } ?: ColorDrawable(Color.TRANSPARENT)
}
private fun addGradientToRecommendationAlbum(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
index d1713b5cd2fd..5ecbcb20a58a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaRecsCardViewModel.kt
@@ -30,4 +30,5 @@ data class MediaRecsCardViewModel(
val areTitlesVisible: Boolean,
val areSubtitlesVisible: Boolean,
val gutsMenu: GutsViewModel,
+ val onLocationChanged: (Int) -> Unit,
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt
index d1184b615c95..9c59aa2729b1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaSmartspaceLogger.kt
@@ -71,7 +71,7 @@ class MediaSmartspaceLogger @Inject constructor() {
* @param eventId id of the event. eg: dismiss, click, or seen.
* @param instanceId id to uniquely identify a card.
* @param uid uid for the application that media comes from.
- * @param location location of media carousel holding media card.
+ * @param surface location of media carousel holding media card.
* @param cardinality number of card in carousel.
* @param isRecommendationCard whether media card being logged is a recommendations card.
* @param isSsReactivated indicates resume media card is reactivated by Smartspace
@@ -83,7 +83,7 @@ class MediaSmartspaceLogger @Inject constructor() {
eventId: Int,
instanceId: Int,
uid: Int,
- location: Int,
+ surface: Int,
cardinality: Int,
isRecommendationCard: Boolean = false,
isSsReactivated: Boolean = false,
@@ -96,7 +96,7 @@ class MediaSmartspaceLogger @Inject constructor() {
eventId,
instanceId,
uid,
- surfaces = intArrayOf(location),
+ surfaces = intArrayOf(surface),
cardinality,
isRecommendationCard,
isSsReactivated,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index c974e0dde5e6..850e2e014c57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -16,8 +16,11 @@
package com.android.systemui.media.controls.domain.pipeline
+import android.R
import android.app.smartspace.SmartspaceAction
+import android.graphics.drawable.Icon
import android.os.Bundle
+import android.os.Process
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -36,7 +39,11 @@ import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
import com.android.systemui.media.controls.ui.controller.MediaPlayerData
import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.media.controls.util.MediaSmartspaceLogger
import com.android.systemui.media.controls.util.MediaUiEventLogger
+import com.android.systemui.media.controls.util.SmallHash
+import com.android.systemui.media.controls.util.mediaSmartspaceLogger
+import com.android.systemui.media.controls.util.mockMediaSmartspaceLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.testKosmos
@@ -101,7 +108,12 @@ class MediaDataFilterImplTest : SysuiTestCase() {
private lateinit var dataGuest: MediaData
private lateinit var dataPrivateProfile: MediaData
private val clock = FakeSystemClock()
- private val repository: MediaFilterRepository = kosmos.mediaFilterRepository
+ private val smartspaceLogger = kosmos.mockMediaSmartspaceLogger
+ private val repository: MediaFilterRepository =
+ with(kosmos) {
+ mediaSmartspaceLogger = mockMediaSmartspaceLogger
+ mediaFilterRepository
+ }
private val mediaLoadingLogger = kosmos.mockMediaLoadingLogger
@Before
@@ -146,6 +158,8 @@ class MediaDataFilterImplTest : SysuiTestCase() {
whenever(smartspaceData.packageName).thenReturn(SMARTSPACE_PACKAGE)
whenever(smartspaceData.recommendations)
.thenReturn(listOf(smartspaceMediaRecommendationItem))
+ whenever(smartspaceMediaRecommendationItem.icon)
+ .thenReturn(Icon.createWithResource(context, R.drawable.ic_media_play))
whenever(smartspaceData.headphoneConnectionTimeMillis)
.thenReturn(clock.currentTimeMillis() - 100)
whenever(smartspaceData.instanceId).thenReturn(SMARTSPACE_INSTANCE_ID)
@@ -533,8 +547,22 @@ class MediaDataFilterImplTest : SysuiTestCase() {
@Test
fun onSwipeToDismiss_setsTimedOut() {
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
- mediaDataFilter.onSwipeToDismiss()
-
+ mediaDataFilter.onSwipeToDismiss(1)
+
+ verify(smartspaceLogger, never())
+ .logSmartspaceCardUIEvent(
+ eq(MediaSmartspaceLogger.SMARTSPACE_CARD_DISMISS_EVENT),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ eq(true)
+ )
verify(mediaDataProcessor).setInactive(eq(KEY), eq(true), eq(true))
}
@@ -1053,11 +1081,27 @@ class MediaDataFilterImplTest : SysuiTestCase() {
targetId = SMARTSPACE_KEY,
isActive = true,
packageName = SMARTSPACE_PACKAGE,
- recommendations = listOf(smartspaceMediaRecommendationItem),
+ recommendations =
+ listOf(
+ smartspaceMediaRecommendationItem,
+ smartspaceMediaRecommendationItem,
+ smartspaceMediaRecommendationItem
+ ),
)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data)
- mediaDataFilter.onSwipeToDismiss()
+ mediaDataFilter.onSwipeToDismiss(1)
+
+ verify(smartspaceLogger)
+ .logSmartspaceCardUIEvent(
+ MediaSmartspaceLogger.SMARTSPACE_CARD_DISMISS_EVENT,
+ SmallHash.hash(data.targetId),
+ Process.INVALID_UID,
+ surface = 1,
+ cardinality = 1,
+ isRecommendationCard = true,
+ isSwipeToDismiss = true
+ )
verify(mediaDataProcessor).setRecommendationInactive(eq(SMARTSPACE_KEY))
verify(mediaDataProcessor, never())
.dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong())
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
index 81adefa11c9b..6e650a3c5391 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractorKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media.controls.domain.pipeline.interactor
-import android.content.applicationContext
import com.android.systemui.activityIntentHelper
import com.android.systemui.bluetooth.mockBroadcastDialogController
import com.android.systemui.kosmos.Kosmos
@@ -31,7 +30,6 @@ import com.android.systemui.statusbar.policy.keyguardStateController
val Kosmos.mediaControlInteractor by
Kosmos.Fixture {
MediaControlInteractor(
- applicationContext = applicationContext,
instanceId = mediaInstanceId,
repository = mediaFilterRepository,
mediaDataProcessor = mediaDataProcessor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
index 461eaa24426f..e490b7502894 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/factory/MediaControlInteractorFactoryKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media.controls.domain.pipeline.interactor.factory
-import android.content.applicationContext
import com.android.internal.logging.InstanceId
import com.android.systemui.activityIntentHelper
import com.android.systemui.bluetooth.mockBroadcastDialogController
@@ -34,7 +33,6 @@ val Kosmos.mediaControlInteractorFactory by
object : MediaControlInteractorFactory {
override fun create(instanceId: InstanceId): MediaControlInteractor {
return MediaControlInteractor(
- applicationContext = applicationContext,
instanceId = instanceId,
repository = mediaFilterRepository,
mediaDataProcessor = mediaDataProcessor,