Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 766674fa authored by Michael Mikhail's avatar Michael Mikhail
Browse files

Add smartspace logs for media cards impression

Flag: com.android.systemui.scene_container
Bug: 330897926
Test: atest SystemUiRoboTests:MediaCarouselInteractorTest
Change-Id: I924eb34d433a4ad5531e2754f6614efa16bb3164
parent ef07d450
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -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)
+101 −2
Original line number Diff line number Diff line
@@ -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
    }
}
+8 −8
Original line number Diff line number Diff line
@@ -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 ") },
            )
+48 −9
Original line number Diff line number Diff line
@@ -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,10 +361,47 @@ 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,
+6 −4
Original line number Diff line number Diff line
@@ -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,
Loading