Loading packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt +12 −4 Original line number Diff line number Diff line Loading @@ -52,9 +52,12 @@ class MediaSessionBasedFilter @Inject constructor( private val packageControllers: LinkedHashMap<String, MutableList<MediaController>> = LinkedHashMap() // Keep track of the key used for the session tokens. This information is used to know when // Keep track of the key used for the session tokens. This information is used to know when to // dispatch a removed event so that a media object for a local session will be removed. private val keyedTokens: MutableMap<String, MutableList<MediaSession.Token>> = mutableMapOf() private val keyedTokens: MutableMap<String, MutableSet<MediaSession.Token>> = mutableMapOf() // Keep track of which media session tokens have associated notifications. private val tokensWithNotifications: MutableSet<MediaSession.Token> = mutableSetOf() private val sessionListener = object : MediaSessionManager.OnActiveSessionsChangedListener { override fun onActiveSessionsChanged(controllers: List<MediaController>) { Loading Loading @@ -90,6 +93,9 @@ class MediaSessionBasedFilter @Inject constructor( */ override fun onMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { backgroundExecutor.execute { info.token?.let { tokensWithNotifications.add(it) } val isMigration = oldKey != null && key != oldKey if (isMigration) { keyedTokens.remove(oldKey)?.let { removed -> keyedTokens.put(key, removed) } Loading @@ -99,7 +105,7 @@ class MediaSessionBasedFilter @Inject constructor( tokens -> tokens.add(info.token) } ?: run { val tokens = mutableListOf(info.token) val tokens = mutableSetOf(info.token) keyedTokens.put(key, tokens) } } Loading @@ -110,7 +116,8 @@ class MediaSessionBasedFilter @Inject constructor( } // Limiting search to only apps with a single remote session. val remote = if (remoteControllers?.size == 1) remoteControllers.firstOrNull() else null if (isMigration || remote == null || remote.sessionToken == info.token) { if (isMigration || remote == null || remote.sessionToken == info.token || !tokensWithNotifications.contains(remote.sessionToken)) { // Not filtering in this case. Passing the event along to listeners. dispatchMediaDataLoaded(key, oldKey, info) } else { Loading Loading @@ -159,5 +166,6 @@ class MediaSessionBasedFilter @Inject constructor( packageControllers.put(controller.packageName, tokens) } } tokensWithNotifications.retainAll(controllers.map { it.sessionToken }) } } packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt +40 −1 Original line number Diff line number Diff line Loading @@ -225,7 +225,7 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { @Test fun remoteSession_loadedEventNotFiltered() { // GIVEN a remove session // GIVEN a remote session whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) val controllers = listOf(controller1) whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) Loading Loading @@ -259,6 +259,22 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { verify(mediaListener, never()).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2)) } @Test fun remoteAndLocalSessions_remoteSessionWithoutNotification() { // GIVEN remote and local sessions whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) val controllers = listOf(controller1, controller2) whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) sessionListener.onActiveSessionsChanged(controllers) // WHEN a loaded event is received that matches the local session filter.onMediaDataLoaded(KEY, null, mediaData1) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered because there isn't a notification for the remote // session. verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) } @Test fun remoteAndLocalHaveDifferentKeys_localLoadedEventFiltered() { // GIVEN remote and local sessions Loading @@ -284,6 +300,29 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { verify(mediaListener).onMediaDataRemoved(eq(key2)) } @Test fun remoteAndLocalHaveDifferentKeys_remoteSessionWithoutNotification() { // GIVEN remote and local sessions val key1 = "KEY_1" val key2 = "KEY_2" whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) val controllers = listOf(controller1, controller2) whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) sessionListener.onActiveSessionsChanged(controllers) // WHEN a loaded event is received that matches the local session filter.onMediaDataLoaded(key1, null, mediaData1) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1)) // WHEN a loaded event is received that matches the remote session filter.onMediaDataLoaded(key2, null, mediaData2) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) } @Test fun multipleRemoteSessions_loadedEventNotFiltered() { // GIVEN two remote sessions Loading Loading
packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt +12 −4 Original line number Diff line number Diff line Loading @@ -52,9 +52,12 @@ class MediaSessionBasedFilter @Inject constructor( private val packageControllers: LinkedHashMap<String, MutableList<MediaController>> = LinkedHashMap() // Keep track of the key used for the session tokens. This information is used to know when // Keep track of the key used for the session tokens. This information is used to know when to // dispatch a removed event so that a media object for a local session will be removed. private val keyedTokens: MutableMap<String, MutableList<MediaSession.Token>> = mutableMapOf() private val keyedTokens: MutableMap<String, MutableSet<MediaSession.Token>> = mutableMapOf() // Keep track of which media session tokens have associated notifications. private val tokensWithNotifications: MutableSet<MediaSession.Token> = mutableSetOf() private val sessionListener = object : MediaSessionManager.OnActiveSessionsChangedListener { override fun onActiveSessionsChanged(controllers: List<MediaController>) { Loading Loading @@ -90,6 +93,9 @@ class MediaSessionBasedFilter @Inject constructor( */ override fun onMediaDataLoaded(key: String, oldKey: String?, info: MediaData) { backgroundExecutor.execute { info.token?.let { tokensWithNotifications.add(it) } val isMigration = oldKey != null && key != oldKey if (isMigration) { keyedTokens.remove(oldKey)?.let { removed -> keyedTokens.put(key, removed) } Loading @@ -99,7 +105,7 @@ class MediaSessionBasedFilter @Inject constructor( tokens -> tokens.add(info.token) } ?: run { val tokens = mutableListOf(info.token) val tokens = mutableSetOf(info.token) keyedTokens.put(key, tokens) } } Loading @@ -110,7 +116,8 @@ class MediaSessionBasedFilter @Inject constructor( } // Limiting search to only apps with a single remote session. val remote = if (remoteControllers?.size == 1) remoteControllers.firstOrNull() else null if (isMigration || remote == null || remote.sessionToken == info.token) { if (isMigration || remote == null || remote.sessionToken == info.token || !tokensWithNotifications.contains(remote.sessionToken)) { // Not filtering in this case. Passing the event along to listeners. dispatchMediaDataLoaded(key, oldKey, info) } else { Loading Loading @@ -159,5 +166,6 @@ class MediaSessionBasedFilter @Inject constructor( packageControllers.put(controller.packageName, tokens) } } tokensWithNotifications.retainAll(controllers.map { it.sessionToken }) } }
packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt +40 −1 Original line number Diff line number Diff line Loading @@ -225,7 +225,7 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { @Test fun remoteSession_loadedEventNotFiltered() { // GIVEN a remove session // GIVEN a remote session whenever(controller1.getPlaybackInfo()).thenReturn(remotePlaybackInfo) val controllers = listOf(controller1) whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) Loading Loading @@ -259,6 +259,22 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { verify(mediaListener, never()).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2)) } @Test fun remoteAndLocalSessions_remoteSessionWithoutNotification() { // GIVEN remote and local sessions whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) val controllers = listOf(controller1, controller2) whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) sessionListener.onActiveSessionsChanged(controllers) // WHEN a loaded event is received that matches the local session filter.onMediaDataLoaded(KEY, null, mediaData1) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered because there isn't a notification for the remote // session. verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1)) } @Test fun remoteAndLocalHaveDifferentKeys_localLoadedEventFiltered() { // GIVEN remote and local sessions Loading @@ -284,6 +300,29 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { verify(mediaListener).onMediaDataRemoved(eq(key2)) } @Test fun remoteAndLocalHaveDifferentKeys_remoteSessionWithoutNotification() { // GIVEN remote and local sessions val key1 = "KEY_1" val key2 = "KEY_2" whenever(controller2.getPlaybackInfo()).thenReturn(remotePlaybackInfo) val controllers = listOf(controller1, controller2) whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers) sessionListener.onActiveSessionsChanged(controllers) // WHEN a loaded event is received that matches the local session filter.onMediaDataLoaded(key1, null, mediaData1) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1)) // WHEN a loaded event is received that matches the remote session filter.onMediaDataLoaded(key2, null, mediaData2) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2)) } @Test fun multipleRemoteSessions_loadedEventNotFiltered() { // GIVEN two remote sessions Loading