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

Commit 13028b17 authored by Yiyi Shen's avatar Yiyi Shen
Browse files

[OutputSwitcher] Show remote device in chip for remote session

During PAS, w/o this fix, all media session's output switcher chip will
show "Sharing audio" since broadcast is a device audio level feature (all music audio will be broadcasted) instead of
media session based.

We prioritize the remote device name for remote session in this fix.

Bug: 436455969
Test: atest
Flag: com.android.settingslib.flags.enable_le_audio_sharing
Change-Id: I143357176579d707bbcd9db382b40e1edeaca78b
parent 19f0dc12
Loading
Loading
Loading
Loading
+9 −4
Original line number Diff line number Diff line
@@ -68,15 +68,20 @@ class MediaDeviceLogger @Inject constructor(@MediaDeviceLog private val buffer:
        buffer.log(TAG, LogLevel.DEBUG, { str1 = name }, { "New device name $str1" })
    }

    fun logLocalDevice(sassDevice: MediaDeviceData?, connectedDevice: MediaDeviceData?) {
    fun logLocalDevice(
        broadcastDevice: MediaDeviceData?,
        sassDevice: MediaDeviceData?,
        connectedDevice: MediaDeviceData?,
    ) {
        buffer.log(
            TAG,
            LogLevel.DEBUG,
            {
                str1 = sassDevice?.name?.toString()
                str2 = connectedDevice?.name?.toString()
                str1 = broadcastDevice?.name?.toString()
                str2 = sassDevice?.name?.toString()
                str3 = connectedDevice?.name?.toString()
            },
            { "Local device: $str1 or $str2" }
            { "Local device: $str1 or $str2 or $str3" },
        )
    }

+50 −61
Original line number Diff line number Diff line
@@ -397,70 +397,49 @@ constructor(
        @WorkerThread
        private fun updateCurrent(notifyListeners: Boolean = true) {
            val oldCurrent = current
            val newCurrent =
                if (inBroadcast()) {
                        MediaDeviceData(
                            enabled = enableOutputSwitcherPersonalAudioSharing(),
                            icon = MediaControlDrawables.getLeAudioSharing(context),
                            name = context.getString(R.string.audio_sharing_description),
                            intent = null,
                        )
                    } else {
                        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
                        ) {
            // currentConnectedDevice non-null.
            val connectedDevice = localMediaManager.currentConnectedDevice?.toMediaDeviceData()
            val newCurrent =
                if (controller?.playbackInfo?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
                    val routingSession =
                        mr2manager.get().getRoutingSessionForMediaController(controller)

                            activeDevice =
                    routingSession?.let {
                        val icon =
                            if (it.selectedRoutes.size > 1) {
                                MediaControlDrawables.getGroupDevice(context)
                            } else {
                                            connectedDevice
                                                ?.icon // Single route. We don't change the icon.
                                connectedDevice?.icon // Single route. We don't change the icon.
                            }
                        // For a remote session, always use the current device from
                        // LocalMediaManager. Override with routing session information
                                    // if
                                    // available:
                        // if available:
                        //   - Name: To show the dynamic group name.
                        //   - Icon: To show the group icon if there's more than one
                                    // selected
                                    //           route.
                                    connectedDevice?.copy(
                                        name = it.name ?: connectedDevice.name,
                                        icon = icon,
                                    )
                        // selected route.
                        connectedDevice?.copy(name = it.name ?: connectedDevice.name, icon = icon)
                    }
                        ?: MediaDeviceData(
                                enabled = false,
                                icon = MediaControlDrawables.getHomeDevices(context),
                                        name =
                                            context.getString(R.string.media_seamless_other_device),
                                name = context.getString(R.string.media_seamless_other_device),
                            )
                            logger.logRemoteDevice(routingSession?.name, connectedDevice)
                            .also { logger.logRemoteDevice(routingSession?.name, connectedDevice) }
                } else {
                            // Prefer SASS if available when playback is local.
                    // Prefer broadcast, then SASS, if available when playback is local.
                    val broadcastDevice = getBroadcastDevice()
                    val sassDevice = getSassDevice()
                            activeDevice = sassDevice ?: connectedDevice
                            logger.logLocalDevice(sassDevice, connectedDevice)
                    broadcastDevice
                        ?: (sassDevice ?: connectedDevice).also {
                            logger.logLocalDevice(broadcastDevice, sassDevice, connectedDevice)
                        }

                        activeDevice ?: EMPTY_AND_DISABLED_MEDIA_DEVICE_DATA
                }
                    .also { logger.logNewDeviceName(it?.name?.toString()) }
                    ?: EMPTY_AND_DISABLED_MEDIA_DEVICE_DATA.also {
                        logger.logNewDeviceName(it.name?.toString())
                    }

            val updated = newCurrent == null || !newCurrent.equalsWithoutIcon(oldCurrent)
            if (!started || updated) {
@@ -471,6 +450,16 @@ constructor(
            }
        }

        private fun getBroadcastDevice(): MediaDeviceData? =
            if (inBroadcast())
                MediaDeviceData(
                    enabled = enableOutputSwitcherPersonalAudioSharing(),
                    icon = MediaControlDrawables.getLeAudioSharing(context),
                    name = context.getString(R.string.audio_sharing_description),
                    intent = null,
                )
            else null

        private fun getSassDevice(): MediaDeviceData? {
            val sassDevice = aboutToConnectDeviceOverride ?: return null
            return sassDevice.fullMediaDevice?.toMediaDeviceData()
+50 −3
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.systemui.media.controls.domain.pipeline

import android.bluetooth.BluetoothLeBroadcast
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
@@ -37,6 +36,7 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
import com.android.settingslib.flags.Flags
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_CONNECTING
import com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED
@@ -134,7 +134,6 @@ public class MediaDeviceManagerTest(flags: FlagsParameterization) : SysuiTestCas
    @Mock private lateinit var controller: MediaController
    @Mock private lateinit var playbackInfo: PlaybackInfo
    @Mock private lateinit var configurationController: ConfigurationController
    @Mock private lateinit var bluetoothLeBroadcast: BluetoothLeBroadcast
    @Mock private lateinit var localBluetoothProfileManager: LocalBluetoothProfileManager
    @Mock private lateinit var localBluetoothLeBroadcast: LocalBluetoothLeBroadcast
    @Mock private lateinit var packageManager: PackageManager
@@ -178,7 +177,10 @@ public class MediaDeviceManagerTest(flags: FlagsParameterization) : SysuiTestCas
        whenever(muteAwaitFactory.create(lmm)).thenReturn(muteAwaitManager)
        whenever(lmm.getCurrentConnectedDevice()).thenReturn(device)
        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(routingSession)

        whenever(localBluetoothManager.profileManager).thenReturn(localBluetoothProfileManager)
        whenever(localBluetoothProfileManager.leAudioBroadcastProfile)
            .thenReturn(localBluetoothLeBroadcast)
        whenever(localBluetoothLeBroadcast.isEnabled(null)).thenReturn(false)
        suggestedDeviceInfo =
            SuggestedDeviceInfo.Builder(SUGGESTED_DEVICE_NAME, DEVICE_ID, TYPE_REMOTE_SPEAKER)
                .build()
@@ -654,6 +656,51 @@ public class MediaDeviceManagerTest(flags: FlagsParameterization) : SysuiTestCas
        assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
    fun onDeviceListUpdate_withBroadcastOnAndRemotePlaybackType_usesNonNullRoutingSessionName() {
        // GIVEN a notif is added
        loadMediaAndCaptureDeviceData()
        reset(listener)
        // GIVEN that MR2Manager returns a valid routing session
        whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
        // GIVEN that broadcast is on
        whenever(localBluetoothLeBroadcast.isEnabled(null)).thenReturn(true)
        // WHEN the selected device changes state
        val deviceCallback = captureCallback()
        deviceCallback.onDeviceListUpdate(mutableListOf(device))
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        // THEN device is enabled and name and icon are set to remote device name.
        val data = captureDeviceData(KEY)
        assertThat(data.enabled).isTrue()
        assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
    }

    @Test
    @EnableFlags(
        Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
        com.android.media.flags.Flags.FLAG_ENABLE_OUTPUT_SWITCHER_PERSONAL_AUDIO_SHARING,
    )
    fun onDeviceListUpdate_withBroadcastOn_returnsBroadcastDevice() {
        // GIVEN a notif is added
        loadMediaAndCaptureDeviceData()
        reset(listener)
        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
        // GIVEN that broadcast is on
        whenever(localBluetoothLeBroadcast.isEnabled(null)).thenReturn(true)
        // WHEN the selected device changes state
        val deviceCallback = captureCallback()
        deviceCallback.onDeviceListUpdate(mutableListOf(device))
        fakeBgExecutor.runAllReady()
        fakeFgExecutor.runAllReady()
        // THEN device is enabled and name and icon are set to "Sharing audio".
        val data = captureDeviceData(KEY)
        assertThat(data.enabled).isTrue()
        assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
    }

    @Test
    fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName() {
        // GIVEN that MR2Manager returns a routing session that does not have a name