Merge "[Media SASS] Use the device's address to fetch the full MediaDevice information so that we display the correct icon." into tm-dev am: 0db54b283e am: 26577b6e3d

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17297743

Change-Id: I1a7382104a0ccebeb3fe42c35c180e82da9fbe0f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Caitlin Cassidy
2022-04-05 20:10:09 +00:00
committed by Automerger Merge Worker
5 changed files with 139 additions and 42 deletions

View File

@@ -28,6 +28,7 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
@@ -239,13 +240,24 @@ public class LocalMediaManager implements BluetoothCallback {
/** /**
* Dispatch a change in the about-to-connect device. See * 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( public void dispatchAboutToConnectDeviceAdded(
@Nullable String deviceName, @NonNull String deviceAddress,
@NonNull String deviceName,
@Nullable Drawable deviceIcon) { @Nullable Drawable deviceIcon) {
for (DeviceCallback callback : getCallbacks()) { 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();
} }
} }
@@ -705,13 +717,27 @@ public class LocalMediaManager implements BluetoothCallback {
* connect imminently and should be displayed as the current device in the media player. * connect imminently and should be displayed as the current device in the media player.
* See [AudioManager.muteAwaitConnection] for more details. * See [AudioManager.muteAwaitConnection] for more details.
* *
* @param deviceName the name of the device (displayed to the user). * The information in the most recent callback should override information from any previous
* @param deviceIcon the icon that should be used with the device. * 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( default void onAboutToConnectDeviceAdded(
@Nullable String deviceName, @NonNull String deviceAddress,
@NonNull String deviceName,
@Nullable Drawable deviceIcon @Nullable Drawable deviceIcon
) {} ) {}
/**
* Callback for notifying that we no longer have an about-to-connect device.
*/
default void onAboutToConnectDeviceRemoved() {}
} }
/** /**

View File

@@ -164,7 +164,7 @@ class MediaDeviceManager @Inject constructor(
} }
// A device that is not yet connected but is expected to connect imminently. Because it's // 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. // expected to connect imminently, it should be displayed as the current device.
private var aboutToConnectDeviceOverride: MediaDeviceData? = null private var aboutToConnectDeviceOverride: AboutToConnectDevice? = null
@AnyThread @AnyThread
fun start() = bgExecutor.execute { fun start() = bgExecutor.execute {
@@ -222,22 +222,34 @@ class MediaDeviceManager @Inject constructor(
} }
} }
override fun onAboutToConnectDeviceChanged(deviceName: String?, deviceIcon: Drawable?) { override fun onAboutToConnectDeviceAdded(
aboutToConnectDeviceOverride = if (deviceName == null || deviceIcon == null) { deviceAddress: String,
null deviceName: String,
} else { deviceIcon: Drawable?
MediaDeviceData(enabled = true, deviceIcon, deviceName) ) {
aboutToConnectDeviceOverride = AboutToConnectDevice(
fullMediaDevice = localMediaManager.getMediaDeviceById(deviceAddress),
backupMediaDeviceData = MediaDeviceData(enabled = true, deviceIcon, deviceName)
)
updateCurrent()
} }
override fun onAboutToConnectDeviceRemoved() {
aboutToConnectDeviceOverride = null
updateCurrent() updateCurrent()
} }
@WorkerThread @WorkerThread
private fun updateCurrent() { private fun updateCurrent() {
if (aboutToConnectDeviceOverride != null) { val aboutToConnect = aboutToConnectDeviceOverride
current = aboutToConnectDeviceOverride if (aboutToConnect != null &&
aboutToConnect.fullMediaDevice == null &&
aboutToConnect.backupMediaDeviceData != null) {
// Only use [backupMediaDeviceData] when we don't have [fullMediaDevice].
current = aboutToConnect.backupMediaDeviceData
return return
} }
val device = localMediaManager.currentConnectedDevice val device = aboutToConnect?.fullMediaDevice ?: localMediaManager.currentConnectedDevice
val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) } val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) }
// If we have a controller but get a null route, then don't trust the device // If we have a controller but get a null route, then don't trust the device
@@ -247,3 +259,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 // There should only be one device that's mutedUntilConnection at a time, so we can
// safely override any previous value. // safely override any previous value.
currentMutedDevice = device 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) { if (currentMutedDevice == device && USAGE_MEDIA in mutedUsages) {
currentMutedDevice = null currentMutedDevice = null
localMediaManager.dispatchAboutToConnectDeviceChanged(null, null) localMediaManager.dispatchAboutToConnectDeviceRemoved()
} }
} }
} }
@@ -76,8 +78,8 @@ class MediaMuteAwaitConnectionManager constructor(
val currentDevice = audioManager.mutingExpectedDevice val currentDevice = audioManager.mutingExpectedDevice
if (currentDevice != null) { if (currentDevice != null) {
currentMutedDevice = currentDevice currentMutedDevice = currentDevice
localMediaManager.dispatchAboutToConnectDeviceChanged( localMediaManager.dispatchAboutToConnectDeviceAdded(
currentDevice.name, currentDevice.getIcon() currentDevice.address, currentDevice.name, currentDevice.getIcon()
) )
} }
} }

View File

@@ -265,20 +265,58 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
} }
@Test @Test
fun onAboutToConnectDeviceChangedWithNonNullParams() { fun onAboutToConnectDeviceAdded_findsDeviceInfoFromAddress() {
manager.onMediaDataLoaded(KEY, null, mediaData) manager.onMediaDataLoaded(KEY, null, mediaData)
// Run and reset the executors and listeners so we only focus on new events. // Run and reset the executors and listeners so we only focus on new events.
fakeBgExecutor.runAllReady() fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady() fakeFgExecutor.runAllReady()
reset(listener) 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 // 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 name = "AboutToConnectDeviceName"
val mockIcon = mock(Drawable::class.java) val mockIcon = mock(Drawable::class.java)
deviceCallback.onAboutToConnectDeviceChanged(name, mockIcon) deviceCallback.onAboutToConnectDeviceAdded(address, name, mockIcon)
assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1) 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) val data = captureDeviceData(KEY)
assertThat(data.enabled).isTrue() assertThat(data.enabled).isTrue()
assertThat(data.name).isEqualTo(name) assertThat(data.name).isEqualTo(name)
@@ -286,21 +324,21 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
} }
@Test @Test
fun onAboutToConnectDeviceChangedWithNullParams() { fun onAboutToConnectDeviceAddedThenRemoved_usesNormalDevice() {
manager.onMediaDataLoaded(KEY, null, mediaData) manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady() fakeBgExecutor.runAllReady()
val deviceCallback = captureCallback() val deviceCallback = captureCallback()
// First set a non-null about-to-connect device // First set a non-null about-to-connect device
deviceCallback.onAboutToConnectDeviceChanged( deviceCallback.onAboutToConnectDeviceAdded(
"AboutToConnectDeviceName", mock(Drawable::class.java) "fakeAddress", "AboutToConnectDeviceName", mock(Drawable::class.java)
) )
// Run and reset the executors and listeners so we only focus on new events. // Run and reset the executors and listeners so we only focus on new events.
fakeBgExecutor.runAllReady() fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady() fakeFgExecutor.runAllReady()
reset(listener) reset(listener)
// WHEN the about-to-connect device changes to null // WHEN hasDevice switches to false
deviceCallback.onAboutToConnectDeviceChanged(null, null) deviceCallback.onAboutToConnectDeviceRemoved()
assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1) assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
// THEN the normal device is returned // THEN the normal device is returned
val data = captureDeviceData(KEY) val data = captureDeviceData(KEY)

View File

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