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

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

Merge "Apply media notification comparison technique on legacy code" into main

parents a96e6294 9d3544d2
Loading
Loading
Loading
Loading
+92 −74
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager.Companion.isMediaNotification
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
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
@@ -84,7 +85,7 @@ 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.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.media.controls.util.SmallHash
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
@@ -186,7 +187,6 @@ class LegacyMediaDataManagerImpl(
    private val mediaDeviceManager: MediaDeviceManager,
    mediaDataCombineLatest: MediaDataCombineLatest,
    private val mediaDataFilter: LegacyMediaDataFilterImpl,
    private val activityStarter: ActivityStarter,
    private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
    private var useMediaResumption: Boolean,
    private val useQsMediaPlayer: Boolean,
@@ -197,6 +197,7 @@ class LegacyMediaDataManagerImpl(
    private val smartspaceManager: SmartspaceManager?,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val mediaDataLoader: dagger.Lazy<MediaDataLoader>,
    private val mediaLogger: MediaLogger,
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener, MediaDataManager {

    companion object {
@@ -273,7 +274,6 @@ class LegacyMediaDataManagerImpl(
        mediaDeviceManager: MediaDeviceManager,
        mediaDataCombineLatest: MediaDataCombineLatest,
        mediaDataFilter: LegacyMediaDataFilterImpl,
        activityStarter: ActivityStarter,
        smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
        clock: SystemClock,
        tunerService: TunerService,
@@ -282,6 +282,7 @@ class LegacyMediaDataManagerImpl(
        smartspaceManager: SmartspaceManager?,
        keyguardUpdateMonitor: KeyguardUpdateMonitor,
        mediaDataLoader: dagger.Lazy<MediaDataLoader>,
        mediaLogger: MediaLogger,
    ) : this(
        context,
        // Loading bitmap for UMO background can take longer time, so it cannot run on the default
@@ -301,7 +302,6 @@ class LegacyMediaDataManagerImpl(
        mediaDeviceManager,
        mediaDataCombineLatest,
        mediaDataFilter,
        activityStarter,
        smartspaceMediaDataProvider,
        Utils.useMediaResumption(context),
        Utils.useQsMediaPlayer(context),
@@ -312,6 +312,7 @@ class LegacyMediaDataManagerImpl(
        smartspaceManager,
        keyguardUpdateMonitor,
        mediaDataLoader,
        mediaLogger,
    )

    private val appChangeReceiver =
@@ -564,29 +565,9 @@ class LegacyMediaDataManagerImpl(
            val resumeAction: Runnable? = currentEntry?.resumeAction
            val hasCheckedForResume = currentEntry?.hasCheckedForResume == true
            val active = currentEntry?.active ?: true
            val mediaController = mediaControllerFactory.create(result.token!!)

            // 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,
@@ -615,8 +596,31 @@ class LegacyMediaDataManagerImpl(
                    appUid = result.appUid,
                    isExplicit = result.isExplicit,
                )

            if (isSameMediaData(context, mediaController, mediaData, currentEntry)) {
                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 != currentEntry?.playbackLocation) {
                logger.logPlaybackLocationChange(
                    result.appUid,
                    sbn.packageName,
                    instanceId,
                    result.playbackLocation
                )
            }

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

    /** Add a listener for changes in this class */
@@ -1100,22 +1104,12 @@ class LegacyMediaDataManagerImpl(
        val instanceId = currentEntry?.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 resumeAction: Runnable? = mediaEntries[key]?.resumeAction
        val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
        val active = mediaEntries[key]?.active ?: true
            onMediaDataLoaded(
                key,
                oldKey,
        var mediaData =
            MediaData(
                sbn.normalizedUserId,
                true,
@@ -1143,8 +1137,32 @@ class LegacyMediaDataManagerImpl(
                instanceId = instanceId,
                appUid = appUid,
                isExplicit = isExplicit,
                smartspaceId = SmallHash.hash(appUid + systemClock.currentTimeMillis().toInt()),
            )

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

        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)
        }

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

+24 −10
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import androidx.annotation.WorkerThread
import androidx.core.view.GestureDetectorCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.android.systemui.Flags
import com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.FalsingManager
@@ -102,9 +103,11 @@ constructor(
            }
            _progress.postValue(value)
        }

    private val _progress = MutableLiveData<Progress>().apply { postValue(_data) }
    val progress: LiveData<Progress>
        get() = _progress

    private var controller: MediaController? = null
        set(value) {
            if (field?.sessionToken != value?.sessionToken) {
@@ -113,6 +116,7 @@ constructor(
                field = value
            }
        }

    private var playbackState: PlaybackState? = null
    private var callback =
        object : MediaController.Callback() {
@@ -128,6 +132,15 @@ constructor(
            override fun onSessionDestroyed() {
                clearController()
            }

            override fun onMetadataChanged(metadata: MediaMetadata?) {
                if (!Flags.mediaControlsPostsOptimization()) return

                val (enabled, duration) = getEnabledStateAndDuration(metadata)
                if (_data.duration != duration) {
                    _data = _data.copy(enabled = enabled, duration = duration)
                }
            }
        }
    private var cancel: Runnable? = null

@@ -233,22 +246,13 @@ constructor(
    fun updateController(mediaController: MediaController?) {
        controller = mediaController
        playbackState = controller?.playbackState
        val mediaMetadata = controller?.metadata
        val (enabled, duration) = getEnabledStateAndDuration(controller?.metadata)
        val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L
        val position = playbackState?.position?.toInt()
        val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
        val playing =
            NotificationMediaManager.isPlayingState(
                playbackState?.state ?: PlaybackState.STATE_NONE
            )
        val enabled =
            if (
                playbackState == null ||
                    playbackState?.getState() == PlaybackState.STATE_NONE ||
                    (duration <= 0)
            )
                false
            else true
        _data = Progress(enabled, seekAvailable, playing, scrubbing, position, duration, listening)
        checkIfPollingNeeded()
    }
@@ -368,6 +372,16 @@ constructor(
        }
    }

    /** returns a pair of whether seekbar is enabled and the duration of media. */
    private fun getEnabledStateAndDuration(metadata: MediaMetadata?): Pair<Boolean, Int> {
        val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() ?: 0
        val enabled =
            !(playbackState == null ||
                playbackState?.state == PlaybackState.STATE_NONE ||
                (duration <= 0))
        return Pair(enabled, duration)
    }

    /**
     * This method specifies if user made a bad seekbar grab or not. If the vertical distance from
     * first touch on seekbar is more than the horizontal distance, this means that the seekbar grab
+45 −4
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.net.Uri
import android.os.Bundle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
import android.service.notification.StatusBarNotification
@@ -61,6 +63,8 @@ import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.domain.resume.MediaResumeListener
import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
import com.android.systemui.media.controls.shared.mediaLogger
import com.android.systemui.media.controls.shared.mockMediaLogger
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.MediaData
@@ -69,7 +73,6 @@ import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvi
import com.android.systemui.media.controls.util.MediaUiEventLogger
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
@@ -186,11 +189,10 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
        mSetFlagsRule.setFlagsParameterization(flags)
    }

    private val kosmos = testKosmos()
    private val kosmos = testKosmos().apply { mediaLogger = mockMediaLogger }
    private val testDispatcher = kosmos.testDispatcher
    private val testScope = kosmos.testScope
    private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
    private val activityStarter = kosmos.activityStarter
    private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
    private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)

@@ -240,7 +242,6 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
                mediaDeviceManager = mediaDeviceManager,
                mediaDataCombineLatest = mediaDataCombineLatest,
                mediaDataFilter = mediaDataFilter,
                activityStarter = activityStarter,
                smartspaceMediaDataProvider = smartspaceMediaDataProvider,
                useMediaResumption = true,
                useQsMediaPlayer = true,
@@ -251,6 +252,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
                smartspaceManager = smartspaceManager,
                keyguardUpdateMonitor = keyguardUpdateMonitor,
                mediaDataLoader = { kosmos.mediaDataLoader },
                mediaLogger = kosmos.mediaLogger,
            )
        verify(tunerService)
            .addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
@@ -2404,6 +2406,45 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa
        assertThat(mediaDataCaptor.value.artwork).isNull()
    }

    @Test
    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_POSTS_OPTIMIZATION)
    fun postDuplicateNotification_doesNotCallListeners() {
        addNotificationAndLoad()
        reset(listener)
        mediaDataManager.onNotificationAdded(KEY, mediaNotification)

        testScope.assertRunAllReady(foreground = 0, background = 1)
        verify(listener, never())
            .onMediaDataLoaded(
                eq(KEY),
                eq(KEY),
                capture(mediaDataCaptor),
                eq(true),
                eq(0),
                eq(false)
            )
        verify(kosmos.mediaLogger).logDuplicateMediaNotification(eq(KEY))
    }

    @Test
    @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_POSTS_OPTIMIZATION)
    fun postDuplicateNotification_callsListeners() {
        addNotificationAndLoad()
        reset(listener)
        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
        testScope.assertRunAllReady(foreground = 1, background = 1)
        verify(listener)
            .onMediaDataLoaded(
                eq(KEY),
                eq(KEY),
                capture(mediaDataCaptor),
                eq(true),
                eq(0),
                eq(false)
            )
        verify(kosmos.mediaLogger, never()).logDuplicateMediaNotification(eq(KEY))
    }

    private fun TestScope.assertRunAllReady(foreground: Int = 0, background: Int = 0) {
        runCurrent()
        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {