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

Commit 47fa996f authored by Jieru Shi's avatar Jieru Shi
Browse files

Add impression logging for media recommendation card

Working sheet to figure out where to log impression and test scenarios https://docs.google.com/spreadsheets/d/1XZbRaS6vCWfSDwZ9prxiv5RFzdSg3a86C0zB-uSgJ-M/edit?resourcekey=0--PdUHvg7ZJ2NgbSkLs3abQ#gid=0

Bug: 181364757
Test: will append statsd_drive logs
Change-Id: I650d7c960b6e26efb39a32e5eacef01339e351a4
parent e3e92e26
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Utils
@@ -156,6 +157,13 @@ class MediaCarouselController @Inject constructor(
        }
    }

    var visibleToUser: Boolean = false
        set(value) {
            if (field != value) {
                field = value
            }
        }

    init {
        mediaFrame = inflateMediaCarousel()
        mediaCarousel = mediaFrame.requireViewById(R.id.media_carousel_scroller)
@@ -203,6 +211,9 @@ class MediaCarouselController @Inject constructor(
            override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
                Log.d(TAG, "My Smartspace media update is here")
                addSmartspaceMediaRecommendations(key, data)
                if (visibleToUser) {
                    logSmartspaceImpression()
                }
            }

            override fun onMediaDataRemoved(key: String) {
@@ -567,6 +578,29 @@ class MediaCarouselController @Inject constructor(
            mediaCarouselScrollHandler.playerWidthPlusPadding = playerWidthPlusPadding
        }
    }

    /**
     * Log the user impression for media card.
     */
    fun logSmartspaceImpression() {
        MediaPlayerData.players().forEach {
            // Log every impression of media recommendation card since it will only be shown
            // for 1 minute after each connection.
            if (it.recommendationViewHolder?.recommendations?.visibility == View.VISIBLE) {
                /* ktlint-disable max-line-length */
                SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
                        800, // SMARTSPACE_CARD_SEEN
                        it.getInstanceId(),
                        SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__HEADPHONE_MEDIA_RECOMMENDATIONS,
                        it.getSurfaceForSmartspaceLogging(),
                        /* rank */ 0,
                        /* cardinality */ 1)
                /* ktlint-disable max-line-length */
            }

            // TODO(shijieru): add logging for media control card
        }
    }
}

@VisibleForTesting
+16 −6
Original line number Diff line number Diff line
@@ -104,6 +104,8 @@ public class MediaControlPanel {
    private int mBackgroundColor;
    private int mDevicePadding;
    private int mAlbumArtSize;
    // Instance id for logging purpose.
    private int mInstanceId;
    private final MediaOutputDialogFactory mMediaOutputDialogFactory;

    /**
@@ -472,6 +474,7 @@ public class MediaControlPanel {
        mRecommendationViewHolder.getCardIcon().setColorFilter(primaryColor);
        mRecommendationViewHolder.getCardText().setTextColor(primaryColor);

        mInstanceId = target.getSmartspaceTargetId().hashCode();
        mRecommendationViewHolder.getRecommendations()
                .setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
        mBackgroundColor = backgroundColor;
@@ -530,7 +533,6 @@ public class MediaControlPanel {
            setSmartspaceRecItemOnClickListener(
                    mediaCoverImageView,
                    recommendation,
                    target.getSmartspaceTargetId(),
                    null);

            if (uiComponentIndex < MEDIA_RECOMMENDATION_ITEMS_PER_ROW) {
@@ -649,7 +651,6 @@ public class MediaControlPanel {
    private void setSmartspaceRecItemOnClickListener(
            @NonNull View view,
            @NonNull SmartspaceAction action,
            @NonNull String targetId,
            @Nullable View.OnClickListener callback) {
        if (view == null || action == null || action.getIntent() == null) {
            Log.e(TAG, "No tap action can be set up");
@@ -660,11 +661,11 @@ public class MediaControlPanel {
            // When media recommendation card is shown, there could be only one card.
            SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
                    760, // SMARTSPACE_CARD_CLICK
                    targetId.hashCode(),
                    mInstanceId,
                    SysUiStatsLog
                            .SMART_SPACE_CARD_REPORTED__CARD_TYPE__HEADPHONE_MEDIA_RECOMMENDATIONS,
                    getSurfaceForSmartspaceLogging(mMediaViewController.getCurrentEndLocation()),
                    /* rank */ 1,
                    getSurfaceForSmartspaceLogging(),
                    /* rank */ 0,
                    /* cardinality */ 1);

            if (shouldSmartspaceRecItemOpenInForeground(action)) {
@@ -708,7 +709,12 @@ public class MediaControlPanel {
        return false;
    }

    private int getSurfaceForSmartspaceLogging(int currentEndLocation) {
    /**
     * Get the surface given the current end location for MediaViewController
     * @return surface used for Smartspace logging
     */
    protected int getSurfaceForSmartspaceLogging() {
        int currentEndLocation = mMediaViewController.getCurrentEndLocation();
        if (currentEndLocation == MediaHierarchyManager.LOCATION_QQS
                || currentEndLocation == MediaHierarchyManager.LOCATION_QS) {
            return SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE;
@@ -717,4 +723,8 @@ public class MediaControlPanel {
        }
        return SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DEFAULT_SURFACE;
    }

    protected int getInstanceId() {
        return mInstanceId;
    }
}
+76 −1
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import javax.inject.Inject
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager

/**
 * Similarly to isShown but also excludes views that have 0 alpha
@@ -73,7 +74,8 @@ class MediaHierarchyManager @Inject constructor(
    private val bypassController: KeyguardBypassController,
    private val mediaCarouselController: MediaCarouselController,
    private val notifLockscreenUserManager: NotificationLockscreenUserManager,
    wakefulnessLifecycle: WakefulnessLifecycle
    wakefulnessLifecycle: WakefulnessLifecycle,
    private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager
) {
    /**
     * The root overlay of the hierarchy. This is where the media notification is attached to
@@ -161,6 +163,26 @@ class MediaHierarchyManager @Inject constructor(
            }
        }

    /**
     * Is quick setting expanded?
     */
    var qsExpanded: Boolean = false
        set(value) {
            if (field != value) {
                field = value
            }
            // Pull down shade from lock screen (exclude the case when shade is brought out by
            // tapping twice on lock screen)
            if (value && isLockScreenShadeVisibleToUser()) {
                mediaCarouselController.logSmartspaceImpression()
            }
            // Release shade and back to lock screen
            if (isLockScreenVisibleToUser()) {
                mediaCarouselController.logSmartspaceImpression()
            }
            mediaCarouselController.visibleToUser = isVisibleToUser()
        }

    /**
     * Is the shade currently collapsing from the expanded qs? If we're on the lockscreen and in qs,
     * we wouldn't want to transition in that case.
@@ -231,6 +253,11 @@ class MediaHierarchyManager @Inject constructor(

            override fun onStateChanged(newState: Int) {
                updateTargetState()
                // Enters shade from lock screen
                if (newState == StatusBarState.SHADE_LOCKED && isLockScreenShadeVisibleToUser()) {
                    mediaCarouselController.logSmartspaceImpression()
                }
                mediaCarouselController.visibleToUser = isVisibleToUser()
            }

            override fun onDozeAmountChanged(linear: Float, eased: Float) {
@@ -240,9 +267,27 @@ class MediaHierarchyManager @Inject constructor(
            override fun onDozingChanged(isDozing: Boolean) {
                if (!isDozing) {
                    dozeAnimationRunning = false
                    // Enters lock screen from screen off
                    if (isLockScreenVisibleToUser()) {
                        mediaCarouselController.logSmartspaceImpression()
                    }
                } else {
                    updateDesiredLocation()
                    qsExpanded = false
                }
                mediaCarouselController.visibleToUser = isVisibleToUser()
            }

            override fun onExpandedChanged(isExpanded: Boolean) {
                // Enters shade from home screen
                if (isHomeScreenShadeVisibleToUser()) {
                    mediaCarouselController.logSmartspaceImpression()
                }
                // Back to lock screen from bouncer
                if (isLockScreenVisibleToUser()) {
                    mediaCarouselController.logSmartspaceImpression()
                }
                mediaCarouselController.visibleToUser = isVisibleToUser()
            }
        })

@@ -622,6 +667,36 @@ class MediaHierarchyManager @Inject constructor(
        return location
    }

    /**
     * Returns true when the media card could be visible to the user if existed.
     */
    private fun isVisibleToUser(): Boolean {
        return isLockScreenVisibleToUser() || isLockScreenShadeVisibleToUser() ||
                isHomeScreenShadeVisibleToUser()
    }

    private fun isLockScreenVisibleToUser(): Boolean {
        return !statusBarStateController.isDozing &&
                !statusBarKeyguardViewManager.isBouncerShowing &&
                statusBarStateController.state == StatusBarState.KEYGUARD &&
                notifLockscreenUserManager.shouldShowLockscreenNotifications() &&
                statusBarStateController.isExpanded &&
                !qsExpanded
    }

    private fun isLockScreenShadeVisibleToUser(): Boolean {
        return !statusBarStateController.isDozing &&
                !statusBarKeyguardViewManager.isBouncerShowing &&
                (statusBarStateController.state == StatusBarState.SHADE_LOCKED ||
                        (statusBarStateController.state == StatusBarState.KEYGUARD && qsExpanded))
    }

    private fun isHomeScreenShadeVisibleToUser(): Boolean {
        return !statusBarStateController.isDozing &&
                statusBarStateController.state == StatusBarState.SHADE &&
                statusBarStateController.isExpanded
    }

    companion object {
        /**
         * Attached in expanded quick settings
+1 −0
Original line number Diff line number Diff line
@@ -2698,6 +2698,7 @@ public class NotificationPanelViewController extends PanelViewController {
        mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
        mIsExpanding = false;
        mMediaHierarchyManager.setCollapsingShadeFromQS(false);
        mMediaHierarchyManager.setQsExpanded(mQsExpanded);
        if (isFullyCollapsed()) {
            DejankUtils.postAfterTraversal(new Runnable() {
                @Override
+7 −3
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import org.junit.Assert.assertNotNull
@@ -73,6 +74,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
    private lateinit var mediaCarouselController: MediaCarouselController
    @Mock
    private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
    @Mock
    private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
    @Captor
    private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
    @JvmField
@@ -90,7 +93,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
                bypassController,
                mediaCarouselController,
                notificationLockscreenUserManager,
                wakefulnessLifecycle)
                wakefulnessLifecycle,
                statusBarKeyguardViewManager)
        verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
        setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN)
        setupHost(qsHost, MediaHierarchyManager.LOCATION_QS)
@@ -98,7 +102,6 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
        `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
        // We'll use the viewmanager to verify a few calls below, let's reset this.
        clearInvocations(mediaCarouselController)

    }

    private fun setupHost(host: MediaHost, location: Int) {
@@ -125,7 +128,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
        observer.onStartedGoingToSleep()
        clearInvocations(mediaCarouselController)
        mediaHiearchyManager.qsExpansion = 0.0f
        verify(mediaCarouselController, times(0)).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
        verify(mediaCarouselController, times(0))
                .onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
    }