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

Commit 142427e0 authored by Michael Mikhail's avatar Michael Mikhail
Browse files

Switch usermap from key to instanceId.

Since keys can change over time, it is better to have a unique field
that maps to its corresponding media data. This will reduce the
updates in current user media data map.

Flag: ACONFIG media_controls_refactor DISABLED
Bug: 326281896
Test: Build.
Test: atest MediaDataFilterImplTest
Test: atest SystemUiRoboTests:MediaFilterRepositoryTest.
Test: atest SystemUiRoboTests:MediaCarouselInteractorTest.
Change-Id: Ie70df444a64d0329c5cb7e9ee343236c8e29ae1c
parent ab5dc479
Loading
Loading
Loading
Loading
+18 −14
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.media.controls.data.repository


import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.testScope
@@ -44,16 +45,17 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
        testScope.runTest {
        testScope.runTest {
            val selectedUserEntries by collectLastValue(underTest.selectedUserEntries)
            val selectedUserEntries by collectLastValue(underTest.selectedUserEntries)


            val userMedia = MediaData().copy(active = true)
            val instanceId = InstanceId.fakeInstanceId(123)
            val userMedia = MediaData().copy(active = true, instanceId = instanceId)


            underTest.addSelectedUserMediaEntry(KEY, userMedia)
            underTest.addSelectedUserMediaEntry(userMedia)


            assertThat(selectedUserEntries?.get(KEY)).isEqualTo(userMedia)
            assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)


            underTest.addSelectedUserMediaEntry(KEY, userMedia.copy(active = false))
            underTest.addSelectedUserMediaEntry(userMedia.copy(active = false))


            assertThat(selectedUserEntries?.get(KEY)).isNotEqualTo(userMedia)
            assertThat(selectedUserEntries?.get(instanceId)).isNotEqualTo(userMedia)
            assertThat(selectedUserEntries?.get(KEY)?.active).isFalse()
            assertThat(selectedUserEntries?.get(instanceId)?.active).isFalse()
        }
        }


    @Test
    @Test
@@ -61,13 +63,14 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
        testScope.runTest {
        testScope.runTest {
            val selectedUserEntries by collectLastValue(underTest.selectedUserEntries)
            val selectedUserEntries by collectLastValue(underTest.selectedUserEntries)


            val userMedia = MediaData()
            val instanceId = InstanceId.fakeInstanceId(123)
            val userMedia = MediaData().copy(instanceId = instanceId)


            underTest.addSelectedUserMediaEntry(KEY, userMedia)
            underTest.addSelectedUserMediaEntry(userMedia)


            assertThat(selectedUserEntries?.get(KEY)).isEqualTo(userMedia)
            assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)


            assertThat(underTest.removeSelectedUserMediaEntry(KEY, userMedia)).isTrue()
            assertThat(underTest.removeSelectedUserMediaEntry(instanceId, userMedia)).isTrue()
        }
        }


    @Test
    @Test
@@ -75,13 +78,14 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
        testScope.runTest {
        testScope.runTest {
            val selectedUserEntries by collectLastValue(underTest.selectedUserEntries)
            val selectedUserEntries by collectLastValue(underTest.selectedUserEntries)


            val userMedia = MediaData()
            val instanceId = InstanceId.fakeInstanceId(123)
            val userMedia = MediaData().copy(instanceId = instanceId)


            underTest.addSelectedUserMediaEntry(KEY, userMedia)
            underTest.addSelectedUserMediaEntry(userMedia)


            assertThat(selectedUserEntries?.get(KEY)).isEqualTo(userMedia)
            assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)


            assertThat(underTest.removeSelectedUserMediaEntry(KEY)).isEqualTo(userMedia)
            assertThat(underTest.removeSelectedUserMediaEntry(instanceId)).isEqualTo(userMedia)
        }
        }


    @Test
    @Test
+7 −6
Original line number Original line Diff line number Diff line
@@ -56,13 +56,13 @@ class MediaCarouselInteractorTest : SysuiTestCase() {


            val userMedia = MediaData().copy(active = true)
            val userMedia = MediaData().copy(active = true)


            mediaFilterRepository.addSelectedUserMediaEntry(KEY, userMedia)
            mediaFilterRepository.addSelectedUserMediaEntry(userMedia)


            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasActiveMedia).isTrue()
            assertThat(hasActiveMedia).isTrue()
            assertThat(hasAnyMedia).isTrue()
            assertThat(hasAnyMedia).isTrue()


            mediaFilterRepository.addSelectedUserMediaEntry(KEY, userMedia.copy(active = false))
            mediaFilterRepository.addSelectedUserMediaEntry(userMedia.copy(active = false))


            assertThat(hasActiveMediaOrRecommendation).isFalse()
            assertThat(hasActiveMediaOrRecommendation).isFalse()
            assertThat(hasActiveMedia).isFalse()
            assertThat(hasActiveMedia).isFalse()
@@ -78,14 +78,16 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
            val hasAnyMedia by collectLastValue(underTest.hasAnyMedia)
            val hasAnyMedia by collectLastValue(underTest.hasAnyMedia)


            val userMedia = MediaData().copy(active = false)
            val userMedia = MediaData().copy(active = false)
            val instanceId = userMedia.instanceId


            mediaFilterRepository.addSelectedUserMediaEntry(KEY, userMedia)
            mediaFilterRepository.addSelectedUserMediaEntry(userMedia)


            assertThat(hasActiveMediaOrRecommendation).isFalse()
            assertThat(hasActiveMediaOrRecommendation).isFalse()
            assertThat(hasActiveMedia).isFalse()
            assertThat(hasActiveMedia).isFalse()
            assertThat(hasAnyMedia).isTrue()
            assertThat(hasAnyMedia).isTrue()


            assertThat(mediaFilterRepository.removeSelectedUserMediaEntry(KEY, userMedia)).isTrue()
            assertThat(mediaFilterRepository.removeSelectedUserMediaEntry(instanceId, userMedia))
                .isTrue()


            assertThat(hasActiveMediaOrRecommendation).isFalse()
            assertThat(hasActiveMediaOrRecommendation).isFalse()
            assertThat(hasActiveMedia).isFalse()
            assertThat(hasActiveMedia).isFalse()
@@ -114,7 +116,7 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasAnyMediaOrRecommendation).isTrue()
            assertThat(hasAnyMediaOrRecommendation).isTrue()


            mediaFilterRepository.addSelectedUserMediaEntry(KEY, userMedia)
            mediaFilterRepository.addSelectedUserMediaEntry(userMedia)


            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasActiveMediaOrRecommendation).isTrue()
            assertThat(hasAnyMediaOrRecommendation).isTrue()
            assertThat(hasAnyMediaOrRecommendation).isTrue()
@@ -193,7 +195,6 @@ class MediaCarouselInteractorTest : SysuiTestCase() {
        testScope.runTest { assertThat(underTest.hasActiveMediaOrRecommendation.value).isFalse() }
        testScope.runTest { assertThat(underTest.hasActiveMediaOrRecommendation.value).isFalse() }


    companion object {
    companion object {
        private const val KEY = "KEY"
        private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
        private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
    }
    }
}
}
+16 −14
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.systemui.media.controls.data.repository
package com.android.systemui.media.controls.data.repository


import com.android.internal.logging.InstanceId
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
@@ -28,17 +29,18 @@ import kotlinx.coroutines.flow.asStateFlow
@SysUISingleton
@SysUISingleton
class MediaFilterRepository @Inject constructor() {
class MediaFilterRepository @Inject constructor() {


    /** Key of media control that recommendations card reactivated. */
    /** Instance id of media control that recommendations card reactivated. */
    private val _reactivatedKey: MutableStateFlow<String?> = MutableStateFlow(null)
    private val _reactivatedId: MutableStateFlow<InstanceId?> = MutableStateFlow(null)
    val reactivatedKey: StateFlow<String?> = _reactivatedKey.asStateFlow()
    val reactivatedId: StateFlow<InstanceId?> = _reactivatedId.asStateFlow()


    private val _smartspaceMediaData: MutableStateFlow<SmartspaceMediaData> =
    private val _smartspaceMediaData: MutableStateFlow<SmartspaceMediaData> =
        MutableStateFlow(SmartspaceMediaData())
        MutableStateFlow(SmartspaceMediaData())
    val smartspaceMediaData: StateFlow<SmartspaceMediaData> = _smartspaceMediaData.asStateFlow()
    val smartspaceMediaData: StateFlow<SmartspaceMediaData> = _smartspaceMediaData.asStateFlow()


    private val _selectedUserEntries: MutableStateFlow<Map<String, MediaData>> =
    private val _selectedUserEntries: MutableStateFlow<Map<InstanceId, MediaData>> =
        MutableStateFlow(LinkedHashMap())
        MutableStateFlow(LinkedHashMap())
    val selectedUserEntries: StateFlow<Map<String, MediaData>> = _selectedUserEntries.asStateFlow()
    val selectedUserEntries: StateFlow<Map<InstanceId, MediaData>> =
        _selectedUserEntries.asStateFlow()


    private val _allUserEntries: MutableStateFlow<Map<String, MediaData>> =
    private val _allUserEntries: MutableStateFlow<Map<String, MediaData>> =
        MutableStateFlow(LinkedHashMap())
        MutableStateFlow(LinkedHashMap())
@@ -62,9 +64,9 @@ class MediaFilterRepository @Inject constructor() {
        return mediaData
        return mediaData
    }
    }


    fun addSelectedUserMediaEntry(key: String, data: MediaData) {
    fun addSelectedUserMediaEntry(data: MediaData) {
        val entries = LinkedHashMap<String, MediaData>(_selectedUserEntries.value)
        val entries = LinkedHashMap<InstanceId, MediaData>(_selectedUserEntries.value)
        entries[key] = data
        entries[data.instanceId] = data
        _selectedUserEntries.value = entries
        _selectedUserEntries.value = entries
    }
    }


@@ -73,8 +75,8 @@ class MediaFilterRepository @Inject constructor() {
     *
     *
     * @return media data if an entry is actually removed, `null` otherwise.
     * @return media data if an entry is actually removed, `null` otherwise.
     */
     */
    fun removeSelectedUserMediaEntry(key: String): MediaData? {
    fun removeSelectedUserMediaEntry(key: InstanceId): MediaData? {
        val entries = LinkedHashMap<String, MediaData>(_selectedUserEntries.value)
        val entries = LinkedHashMap<InstanceId, MediaData>(_selectedUserEntries.value)
        val mediaData = entries.remove(key)
        val mediaData = entries.remove(key)
        _selectedUserEntries.value = entries
        _selectedUserEntries.value = entries
        return mediaData
        return mediaData
@@ -85,8 +87,8 @@ class MediaFilterRepository @Inject constructor() {
     *
     *
     * @return true if media data is removed, false otherwise.
     * @return true if media data is removed, false otherwise.
     */
     */
    fun removeSelectedUserMediaEntry(key: String, data: MediaData): Boolean {
    fun removeSelectedUserMediaEntry(key: InstanceId, data: MediaData): Boolean {
        val entries = LinkedHashMap<String, MediaData>(_selectedUserEntries.value)
        val entries = LinkedHashMap<InstanceId, MediaData>(_selectedUserEntries.value)
        val succeed = entries.remove(key, data)
        val succeed = entries.remove(key, data)
        if (!succeed) {
        if (!succeed) {
            return false
            return false
@@ -105,7 +107,7 @@ class MediaFilterRepository @Inject constructor() {
    }
    }


    /** Updates media control key that recommendations card reactivated. */
    /** Updates media control key that recommendations card reactivated. */
    fun setReactivatedKey(key: String?) {
    fun setReactivatedId(instanceId: InstanceId?) {
        _reactivatedKey.value = key
        _reactivatedId.value = instanceId
    }
    }
}
}
+82 −41
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.os.SystemProperties
import android.util.Log
import android.util.Log
import com.android.internal.annotations.KeepForWeakReference
import com.android.internal.annotations.KeepForWeakReference
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.logging.InstanceId
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
@@ -66,8 +67,8 @@ constructor(
    private val mediaFlags: MediaFlags,
    private val mediaFlags: MediaFlags,
    private val mediaFilterRepository: MediaFilterRepository,
    private val mediaFilterRepository: MediaFilterRepository,
) : MediaDataManager.Listener {
) : MediaDataManager.Listener {
    private val _listeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
    private val _listeners: MutableSet<Listener> = mutableSetOf()
    val listeners: Set<MediaDataManager.Listener>
    val listeners: Set<Listener>
        get() = _listeners.toSet()
        get() = _listeners.toSet()
    lateinit var mediaDataManager: MediaDataManager
    lateinit var mediaDataManager: MediaDataManager


@@ -108,13 +109,10 @@ constructor(
            return
            return
        }
        }


        if (oldKey != null && oldKey != key) {
        mediaFilterRepository.addSelectedUserMediaEntry(data)
            mediaFilterRepository.removeSelectedUserMediaEntry(oldKey)
        }
        mediaFilterRepository.addSelectedUserMediaEntry(key, data)


        // Notify listeners
        // Notify listeners
        listeners.forEach { it.onMediaDataLoaded(key, oldKey, data) }
        listeners.forEach { it.onMediaDataLoaded(data.instanceId) }
    }
    }


    override fun onSmartspaceMediaDataLoaded(
    override fun onSmartspaceMediaDataLoaded(
@@ -160,11 +158,11 @@ constructor(
            // It could happen there are existing active media resume cards, then we don't need to
            // It could happen there are existing active media resume cards, then we don't need to
            // reactivate.
            // reactivate.
            if (shouldReactivate) {
            if (shouldReactivate) {
                val lastActiveKey = sorted.lastKey() // most recently active
                val lastActiveId = sorted.lastKey() // most recently active id
                // Notify listeners to consider this media active
                // Notify listeners to consider this media active
                Log.d(TAG, "reactivating $lastActiveKey instead of smartspace")
                Log.d(TAG, "reactivating $lastActiveId instead of smartspace")
                mediaFilterRepository.setReactivatedKey(lastActiveKey)
                mediaFilterRepository.setReactivatedId(lastActiveId)
                val mediaData = sorted[lastActiveKey]!!.copy(active = true)
                val mediaData = sorted[lastActiveId]!!.copy(active = true)
                logger.logRecommendationActivated(
                logger.logRecommendationActivated(
                    mediaData.appUid,
                    mediaData.appUid,
                    mediaData.packageName,
                    mediaData.packageName,
@@ -172,9 +170,7 @@ constructor(
                )
                )
                listeners.forEach {
                listeners.forEach {
                    it.onMediaDataLoaded(
                    it.onMediaDataLoaded(
                        lastActiveKey,
                        lastActiveId,
                        lastActiveKey,
                        mediaData,
                        receivedSmartspaceCardLatency =
                        receivedSmartspaceCardLatency =
                            (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis)
                            (systemClock.currentTimeMillis() - data.headphoneConnectionTimeMillis)
                                .toInt(),
                                .toInt(),
@@ -196,27 +192,28 @@ constructor(
            smartspaceMediaData.packageName,
            smartspaceMediaData.packageName,
            smartspaceMediaData.instanceId
            smartspaceMediaData.instanceId
        )
        )
        listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
        listeners.forEach { it.onSmartspaceMediaDataLoaded(key, shouldPrioritizeMutable) }
    }
    }


    override fun onMediaDataRemoved(key: String) {
    override fun onMediaDataRemoved(key: String) {
        mediaFilterRepository.removeMediaEntry(key)
        mediaFilterRepository.removeMediaEntry(key)?.let { mediaData ->
        mediaFilterRepository.removeSelectedUserMediaEntry(key)?.let {
            val instanceId = mediaData.instanceId
            mediaFilterRepository.removeSelectedUserMediaEntry(instanceId)?.let {
                // Only notify listeners if something actually changed
                // Only notify listeners if something actually changed
            listeners.forEach { it.onMediaDataRemoved(key) }
                listeners.forEach { it.onMediaDataRemoved(instanceId) }
            }
        }
        }
    }
    }


    override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
    override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
        // First check if we had reactivated media instead of forwarding smartspace
        // First check if we had reactivated media instead of forwarding smartspace
        mediaFilterRepository.reactivatedKey.value?.let {
        mediaFilterRepository.reactivatedId.value?.let { lastActiveId ->
            val lastActiveKey = it
            mediaFilterRepository.setReactivatedId(null)
            mediaFilterRepository.setReactivatedKey(null)
            Log.d(TAG, "expiring reactivated key $lastActiveId")
            Log.d(TAG, "expiring reactivated key $lastActiveKey")
            // Notify listeners to update with actual active value
            // Notify listeners to update with actual active value
            mediaFilterRepository.selectedUserEntries.value[lastActiveKey]?.let { mediaData ->
            mediaFilterRepository.selectedUserEntries.value[lastActiveId]?.let {
                listeners.forEach { listener ->
                listeners.forEach { listener ->
                    listener.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData, immediately)
                    listener.onMediaDataLoaded(lastActiveId, immediately)
                }
                }
            }
            }
        }
        }
@@ -240,8 +237,8 @@ constructor(
            if (!lockscreenUserManager.isProfileAvailable(data.userId)) {
            if (!lockscreenUserManager.isProfileAvailable(data.userId)) {
                // Only remove media when the profile is unavailable.
                // Only remove media when the profile is unavailable.
                if (DEBUG) Log.d(TAG, "Removing $key after profile change")
                if (DEBUG) Log.d(TAG, "Removing $key after profile change")
                mediaFilterRepository.removeSelectedUserMediaEntry(key, data)
                mediaFilterRepository.removeSelectedUserMediaEntry(data.instanceId, data)
                listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
                listeners.forEach { listener -> listener.onMediaDataRemoved(data.instanceId) }
            }
            }
        }
        }
    }
    }
@@ -254,16 +251,16 @@ constructor(
        // Clear the list first, to make sure callbacks from listeners if we have any entries
        // Clear the list first, to make sure callbacks from listeners if we have any entries
        // are up to date
        // are up to date
        mediaFilterRepository.clearSelectedUserMedia()
        mediaFilterRepository.clearSelectedUserMedia()
        keyCopy.forEach {
        keyCopy.forEach { instanceId ->
            if (DEBUG) Log.d(TAG, "Removing $it after user change")
            if (DEBUG) Log.d(TAG, "Removing $instanceId after user change")
            listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) }
            listenersCopy.forEach { listener -> listener.onMediaDataRemoved(instanceId) }
        }
        }


        mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
        mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
            if (lockscreenUserManager.isCurrentProfile(data.userId)) {
            if (lockscreenUserManager.isCurrentProfile(data.userId)) {
                if (DEBUG) Log.d(TAG, "Re-adding $key after user change")
                if (DEBUG) Log.d(TAG, "Re-adding $key after user change")
                mediaFilterRepository.addSelectedUserMediaEntry(key, data)
                mediaFilterRepository.addSelectedUserMediaEntry(data)
                listenersCopy.forEach { listener -> listener.onMediaDataLoaded(key, null, data) }
                listenersCopy.forEach { listener -> listener.onMediaDataLoaded(data.instanceId) }
            }
            }
        }
        }
    }
    }
@@ -271,10 +268,12 @@ constructor(
    /** Invoked when the user has dismissed the media carousel */
    /** Invoked when the user has dismissed the media carousel */
    fun onSwipeToDismiss() {
    fun onSwipeToDismiss() {
        if (DEBUG) Log.d(TAG, "Media carousel swiped away")
        if (DEBUG) Log.d(TAG, "Media carousel swiped away")
        val mediaKeys = mediaFilterRepository.selectedUserEntries.value.keys.toSet()
        val mediaEntries = mediaFilterRepository.allUserEntries.value.entries
        mediaKeys.forEach {
        mediaEntries.forEach { (key, data) ->
            if (mediaFilterRepository.selectedUserEntries.value.containsKey(data.instanceId)) {
                // Force updates to listeners, needed for re-activated card
                // Force updates to listeners, needed for re-activated card
            mediaDataManager.setInactive(it, timedOut = true, forceUpdate = true)
                mediaDataManager.setInactive(key, timedOut = true, forceUpdate = true)
            }
        }
        }
        val smartspaceMediaData = mediaFilterRepository.smartspaceMediaData.value
        val smartspaceMediaData = mediaFilterRepository.smartspaceMediaData.value
        if (smartspaceMediaData.isActive) {
        if (smartspaceMediaData.isActive) {
@@ -312,10 +311,10 @@ constructor(
    }
    }


    /** Add a listener for filtered [MediaData] changes */
    /** Add a listener for filtered [MediaData] changes */
    fun addListener(listener: MediaDataManager.Listener) = _listeners.add(listener)
    fun addListener(listener: Listener) = _listeners.add(listener)


    /** Remove a listener that was registered with addListener */
    /** Remove a listener that was registered with addListener */
    fun removeListener(listener: MediaDataManager.Listener) = _listeners.remove(listener)
    fun removeListener(listener: Listener) = _listeners.remove(listener)


    /**
    /**
     * Return the time since last active for the most-recent media.
     * Return the time since last active for the most-recent media.
@@ -325,15 +324,57 @@ constructor(
     *   the present. MAX_VALUE will be returned if there is no media.
     *   the present. MAX_VALUE will be returned if there is no media.
     */
     */
    private fun timeSinceActiveForMostRecentMedia(
    private fun timeSinceActiveForMostRecentMedia(
        sortedEntries: SortedMap<String, MediaData>
        sortedEntries: SortedMap<InstanceId, MediaData>
    ): Long {
    ): Long {
        if (sortedEntries.isEmpty()) {
        if (sortedEntries.isEmpty()) {
            return Long.MAX_VALUE
            return Long.MAX_VALUE
        }
        }


        val now = systemClock.elapsedRealtime()
        val now = systemClock.elapsedRealtime()
        val lastActiveKey = sortedEntries.lastKey() // most recently active
        val lastActiveInstanceId = sortedEntries.lastKey() // most recently active
        return sortedEntries[lastActiveKey]?.let { now - it.lastActive } ?: Long.MAX_VALUE
        return sortedEntries[lastActiveInstanceId]?.let { now - it.lastActive } ?: Long.MAX_VALUE
    }

    interface Listener {
        /**
         * Called whenever there's new MediaData Loaded for the consumption in views.
         *
         * @param immediately indicates should apply the UI changes immediately, otherwise wait
         *   until the next refresh-round before UI becomes visible. True by default to take in
         *   place immediately.
         * @param receivedSmartspaceCardLatency is the latency between headphone connects and sysUI
         *   displays Smartspace media targets. Will be 0 if the data is not activated by Smartspace
         *   signal.
         * @param isSsReactivated indicates resume media card is reactivated by Smartspace
         *   recommendation signal
         */
        fun onMediaDataLoaded(
            instanceId: InstanceId,
            immediately: Boolean = true,
            receivedSmartspaceCardLatency: Int = 0,
            isSsReactivated: Boolean = false,
        )

        /**
         * Called whenever there's new Smartspace media data loaded.
         *
         * @param shouldPrioritize indicates the sorting priority of the Smartspace card. If true,
         *   it will be prioritized as the first card. Otherwise, it will show up as the last card
         *   as default.
         */
        fun onSmartspaceMediaDataLoaded(key: String, shouldPrioritize: Boolean = false)

        /** Called whenever a previously existing Media notification was removed. */
        fun onMediaDataRemoved(instanceId: InstanceId)

        /**
         * Called whenever a previously existing Smartspace media data was removed.
         *
         * @param immediately indicates should apply the UI changes immediately, otherwise wait
         *   until the next refresh-round before UI becomes visible. True by default to take in
         *   place immediately.
         */
        fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean = true)
    }
    }


    companion object {
    companion object {
+2 −2
Original line number Original line Diff line number Diff line
@@ -27,10 +27,10 @@ import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
interface MediaDataManager {
interface MediaDataManager {


    /** Add a listener for changes in this class */
    /** Add a listener for changes in this class */
    fun addListener(listener: Listener)
    fun addListener(listener: Listener) {}


    /** Remove a listener for changes in this class */
    /** Remove a listener for changes in this class */
    fun removeListener(listener: Listener)
    fun removeListener(listener: Listener) {}


    /**
    /**
     * Called whenever the player has been paused or stopped for a while, or swiped from QQS. This
     * Called whenever the player has been paused or stopped for a while, or swiped from QQS. This
Loading