Loading packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLogger.kt +9 −4 Original line number Diff line number Diff line Loading @@ -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" }, ) } Loading packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt +50 −61 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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() Loading packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt +50 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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() Loading Loading @@ -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 Loading Loading
packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceLogger.kt +9 −4 Original line number Diff line number Diff line Loading @@ -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" }, ) } Loading
packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt +50 −61 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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() Loading
packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt +50 −3 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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() Loading Loading @@ -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 Loading