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

Commit fee56b0f authored by Iván Budnik's avatar Iván Budnik Committed by Android (Google) Code Review
Browse files

Merge "Simplify DeviceChip population logic in MediaDeviceManager" into main

parents 8cd3e21b c64e448d
Loading
Loading
Loading
Loading
+67 −20
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.text.TextUtils
import android.util.Log
import androidx.annotation.AnyThread
@@ -74,6 +75,11 @@ constructor(
    private val listeners: MutableSet<Listener> = mutableSetOf()
    private val entries: MutableMap<String, Entry> = mutableMapOf()

    companion object {
        private val EMPTY_AND_DISABLED_MEDIA_DEVICE_DATA =
            MediaDeviceData(enabled = false, icon = null, name = null, showBroadcastButton = false)
    }

    /** Add a listener for changes to the media route (ie. device). */
    fun addListener(listener: Listener) = listeners.add(listener)

@@ -333,28 +339,32 @@ constructor(
        @WorkerThread
        private fun updateCurrent() {
            if (isLeAudioBroadcastEnabled()) {
                if (enableLeAudioSharing()) {
                    current =
                        MediaDeviceData(
                            enabled = false,
                            icon =
                                context.getDrawable(
                                    com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
                                ),
                            name = context.getString(R.string.audio_sharing_description),
                            intent = null,
                            showBroadcastButton = false
                        )
                current = getLeAudioBroadcastDeviceData()
            } else if (Flags.usePlaybackInfoForRoutingControls()) {
                val activeDevice: MediaDeviceData?

                // LocalMediaManager provides the connected device based on PlaybackInfo.
                // TODO (b/342197065): Simplify nullability once we make currentConnectedDevice
                //  non-null.
                val connectedDevice = localMediaManager.currentConnectedDevice?.toMediaDeviceData()

                if (controller?.playbackInfo?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
                    val routingSession =
                        mr2manager.get().getRoutingSessionForMediaController(controller)

                    activeDevice =
                        routingSession?.let {
                            // For a remote session, always use the current device from
                            // LocalMediaManager. Override with routing session name if available to
                            // show dynamic group name.
                            connectedDevice?.copy(name = it.name ?: connectedDevice.name)
                        }
                } else {
                    current =
                        MediaDeviceData(
                            /* enabled */ true,
                            /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
                            /* name */ broadcastDescription,
                            /* intent */ null,
                            /* showBroadcastButton */ showBroadcastButton = true
                        )
                    // Prefer SASS if available when playback is local.
                    activeDevice = getSassDevice() ?: connectedDevice
                }

                current = activeDevice ?: EMPTY_AND_DISABLED_MEDIA_DEVICE_DATA
            } else {
                val aboutToConnect = aboutToConnectDeviceOverride
                if (
@@ -389,6 +399,43 @@ constructor(
            }
        }

        private fun getSassDevice(): MediaDeviceData? {
            val sassDevice = aboutToConnectDeviceOverride ?: return null
            return sassDevice.fullMediaDevice?.toMediaDeviceData()
                ?: sassDevice.backupMediaDeviceData
        }

        private fun MediaDevice.toMediaDeviceData() =
            MediaDeviceData(
                enabled = true,
                icon = iconWithoutBackground,
                name = name,
                id = id,
                showBroadcastButton = false
            )

        private fun getLeAudioBroadcastDeviceData(): MediaDeviceData {
            return if (enableLeAudioSharing()) {
                MediaDeviceData(
                    enabled = false,
                    icon =
                        context.getDrawable(
                            com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
                        ),
                    name = context.getString(R.string.audio_sharing_description),
                    intent = null,
                    showBroadcastButton = false
                )
            } else {
                MediaDeviceData(
                    enabled = true,
                    icon = context.getDrawable(R.drawable.settings_input_antenna),
                    name = broadcastDescription,
                    intent = null,
                    showBroadcastButton = true
                )
            }
        }
        /** Return a display name for the current device / route, or null if not possible */
        private fun getDeviceName(
            device: MediaDevice?,
+31 −16
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -40,6 +41,7 @@ import com.android.settingslib.flags.Flags
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
import com.android.settingslib.media.flags.Flags.FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.shared.model.MediaData
@@ -101,7 +103,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    @Mock private lateinit var listener: MediaDeviceManager.Listener
    @Mock private lateinit var device: MediaDevice
    @Mock private lateinit var icon: Drawable
    @Mock private lateinit var route: RoutingSessionInfo
    @Mock private lateinit var routingSession: RoutingSessionInfo
    @Mock private lateinit var selectedRoute: MediaRoute2Info
    @Mock private lateinit var controller: MediaController
    @Mock private lateinit var playbackInfo: PlaybackInfo
@@ -141,7 +143,10 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
        whenever(lmmFactory.create(PACKAGE)).thenReturn(lmm)
        whenever(muteAwaitFactory.create(lmm)).thenReturn(muteAwaitManager)
        whenever(lmm.getCurrentConnectedDevice()).thenReturn(device)
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(route)
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(routingSession)

        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
        whenever(controller.playbackInfo).thenReturn(playbackInfo)

        // Create a media sesssion and notification for testing.
        session = MediaSession(context, SESSION_KEY)
@@ -235,6 +240,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
        reset(listener)
        // WHEN media data is loaded with a different token
        // AND that token results in a null route
        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
@@ -394,9 +400,10 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    }

    @Test
    fun deviceNameFromMR2RouteInfo() {
    fun onMediaDataLoaded_withRemotePlaybackType_usesNonNullRoutingSessionName() {
        // GIVEN that MR2Manager returns a valid routing session
        whenever(route.name).thenReturn(REMOTE_DEVICE_NAME)
        whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
        // WHEN a notification is added
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
@@ -408,8 +415,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    }

    @Test
    fun deviceDisabledWhenMR2ReturnsNullRouteInfo() {
    fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
        // GIVEN that MR2Manager returns null for routing session
        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
        // WHEN a notification is added
        manager.onMediaDataLoaded(KEY, null, mediaData)
@@ -422,13 +430,14 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    }

    @Test
    fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceChanged() {
    fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
        // GIVEN a notif is added
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        reset(listener)
        // AND MR2Manager returns null for routing session
        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
        // WHEN the selected device changes state
        val deviceCallback = captureCallback()
@@ -442,13 +451,14 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    }

    @Test
    fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceListUpdate() {
    fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
        // GIVEN a notif is added
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        reset(listener)
        // GIVEN that MR2Manager returns null for routing session
        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
        // WHEN the selected device changes state
        val deviceCallback = captureCallback()
@@ -461,15 +471,17 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
        assertThat(data.name).isNull()
    }

    // With the flag enabled, MediaDeviceManager no longer gathers device name information directly.
    @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
    @Test
    fun mr2ReturnsSystemRouteWithNullName_isPhone_usePhoneName() {
        // When the routing session name is null, and is a system session for a PhoneMediaDevice
        val phoneDevice = mock(PhoneMediaDevice::class.java)
        whenever(phoneDevice.iconWithoutBackground).thenReturn(icon)
        whenever(lmm.currentConnectedDevice).thenReturn(phoneDevice)
        whenever(route.isSystemSession).thenReturn(true)
        whenever(routingSession.isSystemSession).thenReturn(true)

        whenever(route.name).thenReturn(null)
        whenever(routingSession.name).thenReturn(null)
        whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
        whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
        whenever(selectedRoute.type).thenReturn(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)
@@ -483,13 +495,15 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
        assertThat(data.name).isEqualTo(PhoneMediaDevice.getMediaTransferThisDeviceName(context))
    }

    // With the flag enabled, MediaDeviceManager no longer gathers device name information directly.
    @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
    @Test
    fun mr2ReturnsSystemRouteWithNullName_useSelectedRouteName() {
        // When the routing session does not have a name, and is a system session
        whenever(route.name).thenReturn(null)
        whenever(routingSession.name).thenReturn(null)
        whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
        whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
        whenever(route.isSystemSession).thenReturn(true)
        whenever(routingSession.isSystemSession).thenReturn(true)

        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
@@ -503,8 +517,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    @Test
    fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName() {
        // GIVEN that MR2Manager returns a routing session that does not have a name
        whenever(route.name).thenReturn(null)
        whenever(route.isSystemSession).thenReturn(false)
        whenever(routingSession.name).thenReturn(null)
        whenever(routingSession.isSystemSession).thenReturn(false)
        // WHEN a notification is added
        manager.onMediaDataLoaded(KEY, null, mediaData)
        fakeBgExecutor.runAllReady()
@@ -534,7 +548,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
    }

    @Test
    fun audioInfoVolumeControlIdChanged() {
    fun onAudioInfoChanged_withRemotePlaybackInfo_queriesRoutingSession() {
        whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
        whenever(playbackInfo.getVolumeControlId()).thenReturn(null)
        whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
@@ -545,10 +559,11 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
        reset(mr2)
        // WHEN onAudioInfoChanged fires with a volume control id change
        whenever(playbackInfo.getVolumeControlId()).thenReturn("placeholder id")
        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
        val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
        verify(controller).registerCallback(captor.capture())
        captor.value.onAudioInfoChanged(playbackInfo)
        // THEN the route is checked
        // THEN the routing session is checked
        verify(mr2).getRoutingSessionForMediaController(eq(controller))
    }

@@ -654,7 +669,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {

    @Test
    fun testRemotePlaybackDeviceOverride() {
        whenever(route.name).thenReturn(DEVICE_NAME)
        whenever(routingSession.name).thenReturn(DEVICE_NAME)
        val deviceData =
            MediaDeviceData(false, null, REMOTE_DEVICE_NAME, null, showBroadcastButton = false)
        val mediaDataWithDevice = mediaData.copy(device = deviceData)