[Media SASS] Use the device's address to fetch the full MediaDevice

information so that we display the correct icon.

Bug: 223517312
Test: atest MediaDeviceManagerTest, MediaMuteAwaitConnectionManagerTest
Change-Id: If09dce8b2cec5cdd223c46050385ab0aab522d4b
This commit is contained in:
Caitlin Cassidy
2022-03-21 18:35:44 +00:00
parent dda9495566
commit 684fcdf7e9
5 changed files with 139 additions and 42 deletions

View File

@@ -28,6 +28,7 @@ import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@@ -237,13 +238,24 @@ public class LocalMediaManager implements BluetoothCallback {
/**
* Dispatch a change in the about-to-connect device. See
* {@link DeviceCallback#onAboutToConnectDeviceChanged} for more information.
* {@link DeviceCallback#onAboutToConnectDeviceAdded} for more information.
*/
public void dispatchAboutToConnectDeviceChanged(
@Nullable String deviceName,
public void dispatchAboutToConnectDeviceAdded(
@NonNull String deviceAddress,
@NonNull String deviceName,
@Nullable Drawable deviceIcon) {
for (DeviceCallback callback : getCallbacks()) {
callback.onAboutToConnectDeviceChanged(deviceName, deviceIcon);
callback.onAboutToConnectDeviceAdded(deviceAddress, deviceName, deviceIcon);
}
}
/**
* Dispatch a change in the about-to-connect device. See
* {@link DeviceCallback#onAboutToConnectDeviceRemoved} for more information.
*/
public void dispatchAboutToConnectDeviceRemoved() {
for (DeviceCallback callback : getCallbacks()) {
callback.onAboutToConnectDeviceRemoved();
}
}
@@ -703,13 +715,27 @@ public class LocalMediaManager implements BluetoothCallback {
* connect imminently and should be displayed as the current device in the media player.
* See [AudioManager.muteAwaitConnection] for more details.
*
* @param deviceName the name of the device (displayed to the user).
* @param deviceIcon the icon that should be used with the device.
* The information in the most recent callback should override information from any previous
* callbacks.
*
* @param deviceAddress the address of the device. {@see AudioDeviceAttributes.address}.
* If present, we'll use this address to fetch the full information
* about the device (if we can find that information).
* @param deviceName the name of the device (displayed to the user). Used as a backup in
* case using deviceAddress doesn't work.
* @param deviceIcon the icon that should be used with the device. Used as a backup in case
* using deviceAddress doesn't work.
*/
default void onAboutToConnectDeviceChanged(
@Nullable String deviceName,
default void onAboutToConnectDeviceAdded(
@NonNull String deviceAddress,
@NonNull String deviceName,
@Nullable Drawable deviceIcon
) {}
/**
* Callback for notifying that we no longer have an about-to-connect device.
*/
default void onAboutToConnectDeviceRemoved() {}
}
/**

View File

@@ -163,7 +163,7 @@ class MediaDeviceManager @Inject constructor(
}
// A device that is not yet connected but is expected to connect imminently. Because it's
// expected to connect imminently, it should be displayed as the current device.
private var aboutToConnectDeviceOverride: MediaDeviceData? = null
private var aboutToConnectDeviceOverride: AboutToConnectDevice? = null
@AnyThread
fun start() = bgExecutor.execute {
@@ -221,22 +221,34 @@ class MediaDeviceManager @Inject constructor(
}
}
override fun onAboutToConnectDeviceChanged(deviceName: String?, deviceIcon: Drawable?) {
aboutToConnectDeviceOverride = if (deviceName == null || deviceIcon == null) {
null
} else {
MediaDeviceData(enabled = true, deviceIcon, deviceName)
}
override fun onAboutToConnectDeviceAdded(
deviceAddress: String,
deviceName: String,
deviceIcon: Drawable?
) {
aboutToConnectDeviceOverride = AboutToConnectDevice(
fullMediaDevice = localMediaManager.getMediaDeviceById(deviceAddress),
backupMediaDeviceData = MediaDeviceData(enabled = true, deviceIcon, deviceName)
)
updateCurrent()
}
override fun onAboutToConnectDeviceRemoved() {
aboutToConnectDeviceOverride = null
updateCurrent()
}
@WorkerThread
private fun updateCurrent() {
if (aboutToConnectDeviceOverride != null) {
current = aboutToConnectDeviceOverride
return
val aboutToConnect = aboutToConnectDeviceOverride
if (aboutToConnect != null &&
aboutToConnect.fullMediaDevice == null &&
aboutToConnect.backupMediaDeviceData != null) {
// Only use [backupMediaDeviceData] when we don't have [fullMediaDevice].
current = aboutToConnect.backupMediaDeviceData
return
}
val device = localMediaManager.currentConnectedDevice
val device = aboutToConnect?.fullMediaDevice ?: localMediaManager.currentConnectedDevice
val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) }
// If we have a controller but get a null route, then don't trust the device
@@ -246,3 +258,17 @@ class MediaDeviceManager @Inject constructor(
}
}
}
/**
* A class storing information for the about-to-connect device. See
* [LocalMediaManager.DeviceCallback.onAboutToConnectDeviceAdded] for more information.
*
* @property fullMediaDevice a full-fledged [MediaDevice] object representing the device. If
* non-null, prefer using [fullMediaDevice] over [backupMediaDeviceData].
* @property backupMediaDeviceData a backup [MediaDeviceData] object containing the minimum
* information required to display the device. Only use if [fullMediaDevice] is null.
*/
private data class AboutToConnectDevice(
val fullMediaDevice: MediaDevice? = null,
val backupMediaDeviceData: MediaDeviceData? = null
)

View File

@@ -52,7 +52,9 @@ class MediaMuteAwaitConnectionManager constructor(
// There should only be one device that's mutedUntilConnection at a time, so we can
// safely override any previous value.
currentMutedDevice = device
localMediaManager.dispatchAboutToConnectDeviceChanged(device.name, device.getIcon())
localMediaManager.dispatchAboutToConnectDeviceAdded(
device.address, device.name, device.getIcon()
)
}
}
@@ -63,7 +65,7 @@ class MediaMuteAwaitConnectionManager constructor(
) {
if (currentMutedDevice == device && USAGE_MEDIA in mutedUsages) {
currentMutedDevice = null
localMediaManager.dispatchAboutToConnectDeviceChanged(null, null)
localMediaManager.dispatchAboutToConnectDeviceRemoved()
}
}
}
@@ -76,8 +78,8 @@ class MediaMuteAwaitConnectionManager constructor(
val currentDevice = audioManager.mutingExpectedDevice
if (currentDevice != null) {
currentMutedDevice = currentDevice
localMediaManager.dispatchAboutToConnectDeviceChanged(
currentDevice.name, currentDevice.getIcon()
localMediaManager.dispatchAboutToConnectDeviceAdded(
currentDevice.address, currentDevice.name, currentDevice.getIcon()
)
}
}

View File

@@ -284,20 +284,58 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
fun onAboutToConnectDeviceChangedWithNonNullParams() {
fun onAboutToConnectDeviceAdded_findsDeviceInfoFromAddress() {
manager.onMediaDataLoaded(KEY, null, mediaData)
// Run and reset the executors and listeners so we only focus on new events.
fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady()
reset(listener)
val deviceCallback = captureCallback()
// Ensure we'll get device info when using the address
val fullMediaDevice = mock(MediaDevice::class.java)
val address = "fakeAddress"
val nameFromDevice = "nameFromDevice"
val iconFromDevice = mock(Drawable::class.java)
whenever(lmm.getMediaDeviceById(eq(address))).thenReturn(fullMediaDevice)
whenever(fullMediaDevice.name).thenReturn(nameFromDevice)
whenever(fullMediaDevice.iconWithoutBackground).thenReturn(iconFromDevice)
// WHEN the about-to-connect device changes to non-null
val deviceCallback = captureCallback()
val nameFromParam = "nameFromParam"
val iconFromParam = mock(Drawable::class.java)
deviceCallback.onAboutToConnectDeviceAdded(address, nameFromParam, iconFromParam)
assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the about-to-connect device based on the address is returned
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
assertThat(data.name).isEqualTo(nameFromDevice)
assertThat(data.name).isNotEqualTo(nameFromParam)
assertThat(data.icon).isEqualTo(iconFromDevice)
assertThat(data.icon).isNotEqualTo(iconFromParam)
}
@Test
fun onAboutToConnectDeviceAdded_cantFindDeviceInfoFromAddress() {
manager.onMediaDataLoaded(KEY, null, mediaData)
// Run and reset the executors and listeners so we only focus on new events.
fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady()
reset(listener)
// Ensure we can't get device info based on the address
val address = "fakeAddress"
whenever(lmm.getMediaDeviceById(eq(address))).thenReturn(null)
// WHEN the about-to-connect device changes to non-null
val deviceCallback = captureCallback()
val name = "AboutToConnectDeviceName"
val mockIcon = mock(Drawable::class.java)
deviceCallback.onAboutToConnectDeviceChanged(name, mockIcon)
deviceCallback.onAboutToConnectDeviceAdded(address, name, mockIcon)
assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the about-to-connect device is returned
// THEN the about-to-connect device based on the parameters is returned
val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue()
assertThat(data.name).isEqualTo(name)
@@ -305,21 +343,21 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
fun onAboutToConnectDeviceChangedWithNullParams() {
fun onAboutToConnectDeviceAddedThenRemoved_usesNormalDevice() {
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
val deviceCallback = captureCallback()
// First set a non-null about-to-connect device
deviceCallback.onAboutToConnectDeviceChanged(
"AboutToConnectDeviceName", mock(Drawable::class.java)
deviceCallback.onAboutToConnectDeviceAdded(
"fakeAddress", "AboutToConnectDeviceName", mock(Drawable::class.java)
)
// Run and reset the executors and listeners so we only focus on new events.
fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady()
reset(listener)
// WHEN the about-to-connect device changes to null
deviceCallback.onAboutToConnectDeviceChanged(null, null)
// WHEN hasDevice switches to false
deviceCallback.onAboutToConnectDeviceRemoved()
assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the normal device is returned
val data = captureDeviceData(KEY)

View File

@@ -24,7 +24,7 @@ import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION
import android.test.suitebuilder.annotation.SmallTest
import androidx.test.filters.SmallTest
import com.android.settingslib.media.DeviceIconUtil
import com.android.settingslib.media.LocalMediaManager
import com.android.systemui.R
@@ -95,7 +95,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
muteAwaitConnectionManager.startListening()
verify(localMediaManager, never()).dispatchAboutToConnectDeviceChanged(any(), any())
verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any())
}
@Test
@@ -104,7 +104,9 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
muteAwaitConnectionManager.startListening()
verify(localMediaManager).dispatchAboutToConnectDeviceChanged(eq(DEVICE_NAME), eq(icon))
verify(localMediaManager).dispatchAboutToConnectDeviceAdded(
eq(DEVICE_ADDRESS), eq(DEVICE_NAME), eq(icon)
)
}
@Test
@@ -114,7 +116,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_UNKNOWN))
verify(localMediaManager, never()).dispatchAboutToConnectDeviceChanged(any(), any())
verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any())
}
@Test
@@ -125,7 +127,9 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
muteAwaitListener.onMutedUntilConnection(DEVICE, intArrayOf(USAGE_MEDIA))
verify(localMediaManager).dispatchAboutToConnectDeviceChanged(eq(DEVICE_NAME), eq(icon))
verify(localMediaManager).dispatchAboutToConnectDeviceAdded(
eq(DEVICE_ADDRESS), eq(DEVICE_NAME), eq(icon)
)
}
@Test
@@ -135,7 +139,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_MEDIA))
verify(localMediaManager, never()).dispatchAboutToConnectDeviceChanged(any(), any())
verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any())
}
@Test
@@ -155,7 +159,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
)
muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, otherDevice, intArrayOf(USAGE_MEDIA))
verify(localMediaManager, never()).dispatchAboutToConnectDeviceChanged(any(), any())
verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any())
}
@Test
@@ -167,7 +171,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_UNKNOWN))
verify(localMediaManager, never()).dispatchAboutToConnectDeviceChanged(any(), any())
verify(localMediaManager, never()).dispatchAboutToConnectDeviceAdded(any(), any(), any())
}
@Test
@@ -179,7 +183,7 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
muteAwaitListener.onUnmutedEvent(EVENT_CONNECTION, DEVICE, intArrayOf(USAGE_MEDIA))
verify(localMediaManager).dispatchAboutToConnectDeviceChanged(eq(null), eq(null))
verify(localMediaManager).dispatchAboutToConnectDeviceRemoved()
}
private fun getMuteAwaitListener(): AudioManager.MuteAwaitConnectionCallback {
@@ -191,11 +195,12 @@ class MediaMuteAwaitConnectionManagerTest : SysuiTestCase() {
}
}
private const val DEVICE_ADDRESS = "DeviceAddress"
private const val DEVICE_NAME = "DeviceName"
private val DEVICE = AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_USB_HEADSET,
"address",
DEVICE_ADDRESS,
DEVICE_NAME,
listOf(),
listOf(),