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

Commit 9d3544d2 authored by Michael Mikhail's avatar Michael Mikhail
Browse files

Apply media notification comparison technique on legacy code

This CL has a minor fix, to ensure that the duration for added media
notification is updated.

Flag: com.android.systemui.media_controls_posts_optimization
Bug: 358645640
Test: atest LegacyMediaDataManagerImplTest
Test: checked ui and simple perf.
Change-Id: I2980cb9251636105098d604cf8cc4a617e16eb71
parent 8a9e7354
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()) {