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

Commit 227c6c4f authored by Michael Mikhail's avatar Michael Mikhail Committed by Android (Google) Code Review
Browse files

Merge "Prevent duplicate posts from entering pipeline" into main

parents 5159a37b 8a9e7354
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.util.fakeMediaControllerFactory
import com.android.systemui.media.controls.util.mediaFlags
import com.android.systemui.plugins.activityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.testKosmos
@@ -86,7 +85,6 @@ class MediaDataLoaderTest : SysuiTestCase() {
            context,
            testDispatcher,
            testScope,
            kosmos.activityStarter,
            mediaControllerFactory,
            mediaFlags,
            kosmos.imageLoader,
+6 −5
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.shared.model.MediaNotificationAction
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
import com.android.systemui.media.controls.ui.view.MediaViewHolder
@@ -943,7 +944,7 @@ class LegacyMediaDataManagerImpl(
                    desc.subtitle,
                    desc.title,
                    artworkIcon,
                    listOf(mediaAction),
                    listOf(),
                    listOf(0),
                    MediaButton(playOrPause = mediaAction),
                    packageName,
@@ -1074,13 +1075,13 @@ class LegacyMediaDataManagerImpl(
        }

        // Control buttons
        // If flag is enabled and controller has a PlaybackState, create actions from session info
        // If controller has a PlaybackState, create actions from session info
        // Otherwise, use the notification actions
        var actionIcons: List<MediaAction> = emptyList()
        var actionIcons: List<MediaNotificationAction> = emptyList()
        var actionsToShowCollapsed: List<Int> = emptyList()
        val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user)
        if (semanticActions == null) {
            val actions = createActionsFromNotification(context, activityStarter, sbn)
            val actions = createActionsFromNotification(context, sbn)
            actionIcons = actions.first
            actionsToShowCollapsed = actions.second
        }
@@ -1464,7 +1465,7 @@ class LegacyMediaDataManagerImpl(
        val updated =
            data.copy(
                token = null,
                actions = actions,
                actions = listOf(),
                semanticActions = MediaButton(playOrPause = resumeAction),
                actionsToShowInCompact = listOf(0),
                active = false,
+44 −23
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import com.android.systemui.media.controls.domain.pipeline.LegacyMediaDataManage
import com.android.systemui.media.controls.shared.MediaControlDrawables
import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaNotificationAction
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
@@ -217,11 +218,10 @@ private fun includesAction(stateActions: Long, @PlaybackState.Actions action: Lo
/** Generate action buttons based on notification actions */
fun createActionsFromNotification(
    context: Context,
    activityStarter: ActivityStarter,
    sbn: StatusBarNotification
): Pair<List<MediaAction>, List<Int>> {
): Pair<List<MediaNotificationAction>, List<Int>> {
    val notif = sbn.notification
    val actionIcons: MutableList<MediaAction> = ArrayList()
    val actionIcons: MutableList<MediaNotificationAction> = ArrayList()
    val actions = notif.actions
    var actionsToShowCollapsed =
        notif.extras.getIntArray(Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList()
@@ -250,25 +250,6 @@ fun createActionsFromNotification(
                continue
            }

            val runnable =
                action.actionIntent?.let { actionIntent ->
                    Runnable {
                        when {
                            actionIntent.isActivity ->
                                activityStarter.startPendingIntentDismissingKeyguard(
                                    action.actionIntent
                                )
                            action.isAuthenticationRequired ->
                                activityStarter.dismissKeyguardThenExecute(
                                    { sendPendingIntent(action.actionIntent) },
                                    {},
                                    true
                                )
                            else -> sendPendingIntent(actionIntent)
                        }
                    }
                }

            val themeText =
                com.android.settingslib.Utils.getColorAttr(
                        context,
@@ -285,13 +266,53 @@ fun createActionsFromNotification(
                    .setTint(themeText)
                    .loadDrawable(context)

            val mediaAction = MediaAction(mediaActionIcon, runnable, action.title, null)
            val mediaAction =
                MediaNotificationAction(
                    action.isAuthenticationRequired,
                    action.actionIntent,
                    mediaActionIcon,
                    action.title
                )
            actionIcons.add(mediaAction)
        }
    }
    return Pair(actionIcons, actionsToShowCollapsed)
}

/**
 * Converts [MediaNotificationAction] list into [MediaAction] list
 *
 * @param actions list of [MediaNotificationAction]
 * @param activityStarter starter for activities
 * @return list of [MediaAction]
 */
fun getNotificationActions(
    actions: List<MediaNotificationAction>,
    activityStarter: ActivityStarter
): List<MediaAction> {
    return actions.map { action ->
        val runnable =
            action.actionIntent?.let { actionIntent ->
                Runnable {
                    when {
                        actionIntent.isActivity ->
                            activityStarter.startPendingIntentDismissingKeyguard(
                                action.actionIntent
                            )
                        action.isAuthenticationRequired ->
                            activityStarter.dismissKeyguardThenExecute(
                                { sendPendingIntent(action.actionIntent) },
                                {},
                                true
                            )
                        else -> sendPendingIntent(actionIntent)
                    }
                }
            }
        MediaAction(action.icon, runnable, action.contentDescription, background = null)
    }
}

private fun sendPendingIntent(intent: PendingIntent): Boolean {
    return try {
        intent.send(
+6 −8
Original line number Diff line number Diff line
@@ -54,10 +54,10 @@ import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.shared.model.MediaNotificationAction
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
import com.android.systemui.statusbar.notification.row.HybridGroupManager
@@ -80,7 +80,6 @@ constructor(
    @Application val context: Context,
    @Main val mainDispatcher: CoroutineDispatcher,
    @Background val backgroundScope: CoroutineScope,
    private val activityStarter: ActivityStarter,
    private val mediaControllerFactory: MediaControllerFactory,
    private val mediaFlags: MediaFlags,
    private val imageLoader: ImageLoader,
@@ -209,15 +208,14 @@ constructor(
            val device: MediaDeviceData? = getDeviceInfoForRemoteCast(key, sbn)

            // Control buttons
            // If flag is enabled and controller has a PlaybackState, create actions from session
            // info
            // If controller has a PlaybackState, create actions from session info
            // Otherwise, use the notification actions
            var actionIcons: List<MediaAction> = emptyList()
            var actionIcons: List<MediaNotificationAction> = emptyList()
            var actionsToShowCollapsed: List<Int> = emptyList()
            val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user)
            logD(TAG) { "Semantic actions: $semanticActions" }
            if (semanticActions == null) {
                val actions = createActionsFromNotification(context, activityStarter, sbn)
                val actions = createActionsFromNotification(context, sbn)
                actionIcons = actions.first
                actionsToShowCollapsed = actions.second
                logD(TAG) { "[!!] Semantic actions: $semanticActions" }
@@ -329,7 +327,7 @@ constructor(
                artist = desc.subtitle,
                song = desc.title,
                artworkIcon = artworkIcon,
                actionIcons = listOf(mediaAction),
                actionIcons = listOf(),
                actionsToShowInCompact = listOf(0),
                semanticActions = MediaButton(playOrPause = mediaAction),
                token = token,
@@ -514,7 +512,7 @@ constructor(
        val artist: CharSequence?,
        val song: CharSequence?,
        val artworkIcon: Icon?,
        val actionIcons: List<MediaAction>,
        val actionIcons: List<MediaNotificationAction>,
        val actionsToShowInCompact: List<Int>,
        val semanticActions: MediaButton?,
        val token: MediaSession.Token?,
+109 −86
Original line number Diff line number Diff line
@@ -71,12 +71,14 @@ import com.android.systemui.media.controls.data.repository.MediaDataRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager.Companion.isMediaNotification
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
import com.android.systemui.media.controls.shared.MediaLogger
import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
import com.android.systemui.media.controls.shared.model.MediaAction
import com.android.systemui.media.controls.shared.model.MediaButton
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.shared.model.MediaNotificationAction
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
import com.android.systemui.media.controls.ui.view.MediaViewHolder
@@ -149,6 +151,7 @@ class MediaDataProcessor(
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val mediaDataRepository: MediaDataRepository,
    private val mediaDataLoader: dagger.Lazy<MediaDataLoader>,
    private val mediaLogger: MediaLogger,
) : CoreStartable, BcSmartspaceDataPlugin.SmartspaceTargetListener {

    companion object {
@@ -228,6 +231,7 @@ class MediaDataProcessor(
        keyguardUpdateMonitor: KeyguardUpdateMonitor,
        mediaDataRepository: MediaDataRepository,
        mediaDataLoader: dagger.Lazy<MediaDataLoader>,
        mediaLogger: MediaLogger,
    ) : this(
        context,
        applicationScope,
@@ -253,6 +257,7 @@ class MediaDataProcessor(
        keyguardUpdateMonitor,
        mediaDataRepository,
        mediaDataLoader,
        mediaLogger,
    )

    private val appChangeReceiver =
@@ -794,7 +799,7 @@ class MediaDataProcessor(
                    desc.subtitle,
                    desc.title,
                    artworkIcon,
                    listOf(mediaAction),
                    listOf(),
                    listOf(0),
                    MediaButton(playOrPause = mediaAction),
                    packageName,
@@ -832,35 +837,15 @@ class MediaDataProcessor(
                return@withContext
            }

            val currentEntry = mediaDataRepository.mediaEntries.value[key]
            val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
            val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
            val resumeAction: Runnable? = currentEntry?.resumeAction
            val hasCheckedForResume = currentEntry?.hasCheckedForResume == true
            val active = currentEntry?.active ?: true
            val mediaController = mediaControllerFactory.create(result.token!!)
            val oldEntry = mediaDataRepository.mediaEntries.value[key]
            val instanceId = oldEntry?.instanceId ?: logger.getNewInstanceId()
            val createdTimestampMillis = oldEntry?.createdTimestampMillis ?: 0L
            val resumeAction: Runnable? = oldEntry?.resumeAction
            val hasCheckedForResume = oldEntry?.hasCheckedForResume == true
            val active = oldEntry?.active ?: true

            // We need to log the correct media added.
            if (isNewlyActiveEntry) {
                logSingleVsMultipleMediaAdded(result.appUid, sbn.packageName, instanceId)
                logger.logActiveMediaAdded(
                    result.appUid,
                    sbn.packageName,
                    instanceId,
                    result.playbackLocation
                )
            } else if (result.playbackLocation != currentEntry?.playbackLocation) {
                logger.logPlaybackLocationChange(
                    result.appUid,
                    sbn.packageName,
                    instanceId,
                    result.playbackLocation
                )
            }

            withContext(mainDispatcher) {
                onMediaDataLoaded(
                    key,
                    oldKey,
            val mediaData =
                MediaData(
                    userId = sbn.normalizedUserId,
                    initialized = true,
@@ -889,8 +874,31 @@ class MediaDataProcessor(
                    appUid = result.appUid,
                    isExplicit = result.isExplicit,
                )

            if (isSameMediaData(context, mediaController, mediaData, oldEntry)) {
                mediaLogger.logDuplicateMediaNotification(key)
                return@withContext
            }

            // We need to log the correct media added.
            if (isNewlyActiveEntry) {
                logSingleVsMultipleMediaAdded(result.appUid, sbn.packageName, instanceId)
                logger.logActiveMediaAdded(
                    result.appUid,
                    sbn.packageName,
                    instanceId,
                    result.playbackLocation
                )
            } else if (result.playbackLocation != oldEntry?.playbackLocation) {
                logger.logPlaybackLocationChange(
                    result.appUid,
                    sbn.packageName,
                    instanceId,
                    result.playbackLocation
                )
            }

            withContext(mainDispatcher) { onMediaDataLoaded(key, oldKey, mediaData) }
        }

    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
@@ -1001,13 +1009,13 @@ class MediaDataProcessor(
        }

        // Control buttons
        // If flag is enabled and controller has a PlaybackState, create actions from session info
        // If controller has a PlaybackState, create actions from session info
        // Otherwise, use the notification actions
        var actionIcons: List<MediaAction> = emptyList()
        var actionIcons: List<MediaNotificationAction> = emptyList()
        var actionsToShowCollapsed: List<Int> = emptyList()
        val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user)
        if (semanticActions == null) {
            val actions = createActionsFromNotification(context, activityStarter, sbn)
            val actions = createActionsFromNotification(context, sbn)
            actionIcons = actions.first
            actionsToShowCollapsed = actions.second
        }
@@ -1022,27 +1030,17 @@ class MediaDataProcessor(
            else MediaData.PLAYBACK_CAST_LOCAL
        val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) }

        val currentEntry = mediaDataRepository.mediaEntries.value.get(key)
        val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
        val oldEntry = mediaDataRepository.mediaEntries.value.get(key)
        val instanceId = oldEntry?.instanceId ?: logger.getNewInstanceId()
        val appUid = appInfo?.uid ?: Process.INVALID_UID

        if (isNewlyActiveEntry) {
            logSingleVsMultipleMediaAdded(appUid, sbn.packageName, instanceId)
            logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation)
        } else if (playbackLocation != currentEntry?.playbackLocation) {
            logger.logPlaybackLocationChange(appUid, sbn.packageName, instanceId, playbackLocation)
        }

        val lastActive = systemClock.elapsedRealtime()
        val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
        foregroundExecutor.execute {
        val createdTimestampMillis = oldEntry?.createdTimestampMillis ?: 0L
        val resumeAction: Runnable? = mediaDataRepository.mediaEntries.value[key]?.resumeAction
        val hasCheckedForResume =
            mediaDataRepository.mediaEntries.value[key]?.hasCheckedForResume == true
        val active = mediaDataRepository.mediaEntries.value[key]?.active ?: true
            onMediaDataLoaded(
                key,
                oldKey,
        var mediaData =
            MediaData(
                sbn.normalizedUserId,
                true,
@@ -1072,7 +1070,32 @@ class MediaDataProcessor(
                isExplicit = isExplicit,
                smartspaceId = SmallHash.hash(appUid + systemClock.currentTimeMillis().toInt()),
            )

        if (isSameMediaData(context, mediaController, mediaData, oldEntry)) {
            mediaLogger.logDuplicateMediaNotification(key)
            return
        }

        if (isNewlyActiveEntry) {
            logSingleVsMultipleMediaAdded(appUid, sbn.packageName, instanceId)
            logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation)
        } else if (playbackLocation != oldEntry?.playbackLocation) {
            logger.logPlaybackLocationChange(appUid, sbn.packageName, instanceId, playbackLocation)
        }

        foregroundExecutor.execute {
            val oldResumeAction: Runnable? =
                mediaDataRepository.mediaEntries.value[key]?.resumeAction
            val oldHasCheckedForResume =
                mediaDataRepository.mediaEntries.value[key]?.hasCheckedForResume == true
            val oldActive = mediaDataRepository.mediaEntries.value[key]?.active ?: true
            mediaData =
                mediaData.copy(
                    resumeAction = oldResumeAction,
                    hasCheckedForResume = oldHasCheckedForResume,
                    active = oldActive
                )
            onMediaDataLoaded(key, oldKey, mediaData)
        }
    }

@@ -1402,7 +1425,7 @@ class MediaDataProcessor(
        val updated =
            data.copy(
                token = null,
                actions = actions,
                actions = listOf(),
                semanticActions = MediaButton(playOrPause = resumeAction),
                actionsToShowInCompact = listOf(0),
                active = false,
Loading