Loading packages/SystemUI/src/com/android/systemui/media/MediaData.kt +17 −1 Original line number Original line Diff line number Diff line Loading @@ -219,4 +219,20 @@ data class MediaDeviceData /** Whether or not to show the broadcast button */ /** Whether or not to show the broadcast button */ val showBroadcastButton: Boolean val showBroadcastButton: Boolean ) ) { /** * Check whether [MediaDeviceData] objects are equal in all fields except the icon. The icon * is ignored because it can change by reference frequently depending on the device type's * implementation, but this is not usually relevant unless other info has changed */ fun equalsWithoutIcon(other: MediaDeviceData?): Boolean { if (other == null) { return false } return enabled == other.enabled && name == other.name && intent == other.intent && id == other.id } } packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +2 −2 Original line number Original line Diff line number Diff line Loading @@ -168,8 +168,8 @@ class MediaDeviceManager @Inject constructor( private var playbackType = PLAYBACK_TYPE_UNKNOWN private var playbackType = PLAYBACK_TYPE_UNKNOWN private var current: MediaDeviceData? = null private var current: MediaDeviceData? = null set(value) { set(value) { val hasSameId = value?.id != null && value.id == field?.id val sameWithoutIcon = value != null && value.equalsWithoutIcon(field) if (!started || (!hasSameId && value != field)) { if (!started || !sameWithoutIcon) { field = value field = value fgExecutor.execute { fgExecutor.execute { processDevice(key, oldKey, value) processDevice(key, oldKey, value) Loading packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt +86 −0 Original line number Original line Diff line number Diff line Loading @@ -56,7 +56,9 @@ import org.mockito.Mockito.any import org.mockito.Mockito.mock import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.reset import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever Loading @@ -64,6 +66,7 @@ private const val KEY = "TEST_KEY" private const val KEY_OLD = "TEST_KEY_OLD" private const val KEY_OLD = "TEST_KEY_OLD" private const val PACKAGE = "PKG" private const val PACKAGE = "PKG" private const val SESSION_KEY = "SESSION_KEY" private const val SESSION_KEY = "SESSION_KEY" private const val DEVICE_ID = "DEVICE_ID" private const val DEVICE_NAME = "DEVICE_NAME" private const val DEVICE_NAME = "DEVICE_NAME" private const val REMOTE_DEVICE_NAME = "REMOTE_DEVICE_NAME" private const val REMOTE_DEVICE_NAME = "REMOTE_DEVICE_NAME" private const val BROADCAST_APP_NAME = "BROADCAST_APP_NAME" private const val BROADCAST_APP_NAME = "BROADCAST_APP_NAME" Loading Loading @@ -477,6 +480,89 @@ public class MediaDeviceManagerTest : SysuiTestCase() { verify(mr2, never()).getRoutingSessionForMediaController(eq(controller)) verify(mr2, never()).getRoutingSessionForMediaController(eq(controller)) } } @Test fun deviceIdChanged_informListener() { // GIVEN a notification is added, with a particular device connected whenever(device.id).thenReturn(DEVICE_ID) manager.onMediaDataLoaded(KEY, null, mediaData) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() // and later the manager gets a new device ID val deviceCallback = captureCallback() val updatedId = DEVICE_ID + "_new" whenever(device.id).thenReturn(updatedId) deviceCallback.onDeviceListUpdate(mutableListOf(device)) // THEN the listener gets the updated info fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() val dataCaptor = ArgumentCaptor.forClass(MediaDeviceData::class.java) verify(listener, times(2)).onMediaDeviceChanged(eq(KEY), any(), dataCaptor.capture()) val firstDevice = dataCaptor.allValues.get(0) assertThat(firstDevice.id).isEqualTo(DEVICE_ID) val secondDevice = dataCaptor.allValues.get(1) assertThat(secondDevice.id).isEqualTo(updatedId) } @Test fun deviceNameChanged_informListener() { // GIVEN a notification is added, with a particular device connected whenever(device.id).thenReturn(DEVICE_ID) whenever(device.name).thenReturn(DEVICE_NAME) manager.onMediaDataLoaded(KEY, null, mediaData) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() // and later the manager gets a new device name val deviceCallback = captureCallback() val updatedName = DEVICE_NAME + "_new" whenever(device.name).thenReturn(updatedName) deviceCallback.onDeviceListUpdate(mutableListOf(device)) // THEN the listener gets the updated info fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() val dataCaptor = ArgumentCaptor.forClass(MediaDeviceData::class.java) verify(listener, times(2)).onMediaDeviceChanged(eq(KEY), any(), dataCaptor.capture()) val firstDevice = dataCaptor.allValues.get(0) assertThat(firstDevice.name).isEqualTo(DEVICE_NAME) val secondDevice = dataCaptor.allValues.get(1) assertThat(secondDevice.name).isEqualTo(updatedName) } @Test fun deviceIconChanged_doesNotCallListener() { // GIVEN a notification is added, with a particular device connected whenever(device.id).thenReturn(DEVICE_ID) whenever(device.name).thenReturn(DEVICE_NAME) val firstIcon = mock(Drawable::class.java) whenever(device.icon).thenReturn(firstIcon) manager.onMediaDataLoaded(KEY, null, mediaData) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() val dataCaptor = ArgumentCaptor.forClass(MediaDeviceData::class.java) verify(listener).onMediaDeviceChanged(eq(KEY), any(), dataCaptor.capture()) // and later the manager gets a callback with only the icon changed val deviceCallback = captureCallback() val secondIcon = mock(Drawable::class.java) whenever(device.icon).thenReturn(secondIcon) deviceCallback.onDeviceListUpdate(mutableListOf(device)) // THEN the listener is not called again fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() verifyNoMoreInteractions(listener) } @Test @Test fun testRemotePlaybackDeviceOverride() { fun testRemotePlaybackDeviceOverride() { whenever(route.name).thenReturn(DEVICE_NAME) whenever(route.name).thenReturn(DEVICE_NAME) Loading Loading
packages/SystemUI/src/com/android/systemui/media/MediaData.kt +17 −1 Original line number Original line Diff line number Diff line Loading @@ -219,4 +219,20 @@ data class MediaDeviceData /** Whether or not to show the broadcast button */ /** Whether or not to show the broadcast button */ val showBroadcastButton: Boolean val showBroadcastButton: Boolean ) ) { /** * Check whether [MediaDeviceData] objects are equal in all fields except the icon. The icon * is ignored because it can change by reference frequently depending on the device type's * implementation, but this is not usually relevant unless other info has changed */ fun equalsWithoutIcon(other: MediaDeviceData?): Boolean { if (other == null) { return false } return enabled == other.enabled && name == other.name && intent == other.intent && id == other.id } }
packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +2 −2 Original line number Original line Diff line number Diff line Loading @@ -168,8 +168,8 @@ class MediaDeviceManager @Inject constructor( private var playbackType = PLAYBACK_TYPE_UNKNOWN private var playbackType = PLAYBACK_TYPE_UNKNOWN private var current: MediaDeviceData? = null private var current: MediaDeviceData? = null set(value) { set(value) { val hasSameId = value?.id != null && value.id == field?.id val sameWithoutIcon = value != null && value.equalsWithoutIcon(field) if (!started || (!hasSameId && value != field)) { if (!started || !sameWithoutIcon) { field = value field = value fgExecutor.execute { fgExecutor.execute { processDevice(key, oldKey, value) processDevice(key, oldKey, value) Loading
packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt +86 −0 Original line number Original line Diff line number Diff line Loading @@ -56,7 +56,9 @@ import org.mockito.Mockito.any import org.mockito.Mockito.mock import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.reset import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever Loading @@ -64,6 +66,7 @@ private const val KEY = "TEST_KEY" private const val KEY_OLD = "TEST_KEY_OLD" private const val KEY_OLD = "TEST_KEY_OLD" private const val PACKAGE = "PKG" private const val PACKAGE = "PKG" private const val SESSION_KEY = "SESSION_KEY" private const val SESSION_KEY = "SESSION_KEY" private const val DEVICE_ID = "DEVICE_ID" private const val DEVICE_NAME = "DEVICE_NAME" private const val DEVICE_NAME = "DEVICE_NAME" private const val REMOTE_DEVICE_NAME = "REMOTE_DEVICE_NAME" private const val REMOTE_DEVICE_NAME = "REMOTE_DEVICE_NAME" private const val BROADCAST_APP_NAME = "BROADCAST_APP_NAME" private const val BROADCAST_APP_NAME = "BROADCAST_APP_NAME" Loading Loading @@ -477,6 +480,89 @@ public class MediaDeviceManagerTest : SysuiTestCase() { verify(mr2, never()).getRoutingSessionForMediaController(eq(controller)) verify(mr2, never()).getRoutingSessionForMediaController(eq(controller)) } } @Test fun deviceIdChanged_informListener() { // GIVEN a notification is added, with a particular device connected whenever(device.id).thenReturn(DEVICE_ID) manager.onMediaDataLoaded(KEY, null, mediaData) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() // and later the manager gets a new device ID val deviceCallback = captureCallback() val updatedId = DEVICE_ID + "_new" whenever(device.id).thenReturn(updatedId) deviceCallback.onDeviceListUpdate(mutableListOf(device)) // THEN the listener gets the updated info fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() val dataCaptor = ArgumentCaptor.forClass(MediaDeviceData::class.java) verify(listener, times(2)).onMediaDeviceChanged(eq(KEY), any(), dataCaptor.capture()) val firstDevice = dataCaptor.allValues.get(0) assertThat(firstDevice.id).isEqualTo(DEVICE_ID) val secondDevice = dataCaptor.allValues.get(1) assertThat(secondDevice.id).isEqualTo(updatedId) } @Test fun deviceNameChanged_informListener() { // GIVEN a notification is added, with a particular device connected whenever(device.id).thenReturn(DEVICE_ID) whenever(device.name).thenReturn(DEVICE_NAME) manager.onMediaDataLoaded(KEY, null, mediaData) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() // and later the manager gets a new device name val deviceCallback = captureCallback() val updatedName = DEVICE_NAME + "_new" whenever(device.name).thenReturn(updatedName) deviceCallback.onDeviceListUpdate(mutableListOf(device)) // THEN the listener gets the updated info fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() val dataCaptor = ArgumentCaptor.forClass(MediaDeviceData::class.java) verify(listener, times(2)).onMediaDeviceChanged(eq(KEY), any(), dataCaptor.capture()) val firstDevice = dataCaptor.allValues.get(0) assertThat(firstDevice.name).isEqualTo(DEVICE_NAME) val secondDevice = dataCaptor.allValues.get(1) assertThat(secondDevice.name).isEqualTo(updatedName) } @Test fun deviceIconChanged_doesNotCallListener() { // GIVEN a notification is added, with a particular device connected whenever(device.id).thenReturn(DEVICE_ID) whenever(device.name).thenReturn(DEVICE_NAME) val firstIcon = mock(Drawable::class.java) whenever(device.icon).thenReturn(firstIcon) manager.onMediaDataLoaded(KEY, null, mediaData) fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() val dataCaptor = ArgumentCaptor.forClass(MediaDeviceData::class.java) verify(listener).onMediaDeviceChanged(eq(KEY), any(), dataCaptor.capture()) // and later the manager gets a callback with only the icon changed val deviceCallback = captureCallback() val secondIcon = mock(Drawable::class.java) whenever(device.icon).thenReturn(secondIcon) deviceCallback.onDeviceListUpdate(mutableListOf(device)) // THEN the listener is not called again fakeBgExecutor.runAllReady() fakeFgExecutor.runAllReady() verifyNoMoreInteractions(listener) } @Test @Test fun testRemotePlaybackDeviceOverride() { fun testRemotePlaybackDeviceOverride() { whenever(route.name).thenReturn(DEVICE_NAME) whenever(route.name).thenReturn(DEVICE_NAME) Loading