Merge "Update device name on audio info changed" into rvc-qpr-dev

This commit is contained in:
TreeHugger Robot
2020-09-28 11:46:56 +00:00
committed by Android (Google) Code Review
2 changed files with 73 additions and 36 deletions

View File

@@ -16,7 +16,6 @@
package com.android.systemui.media package com.android.systemui.media
import android.content.Context
import android.media.MediaRouter2Manager import android.media.MediaRouter2Manager
import android.media.session.MediaController import android.media.session.MediaController
import androidx.annotation.AnyThread import androidx.annotation.AnyThread
@@ -24,20 +23,22 @@ import androidx.annotation.MainThread
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice import com.android.settingslib.media.MediaDevice
import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.PrintWriter import java.io.PrintWriter
import java.util.concurrent.Executor import java.util.concurrent.Executor
import javax.inject.Inject import javax.inject.Inject
private const val PLAYBACK_TYPE_UNKNOWN = 0
/** /**
* Provides information about the route (ie. device) where playback is occurring. * Provides information about the route (ie. device) where playback is occurring.
*/ */
class MediaDeviceManager @Inject constructor( class MediaDeviceManager @Inject constructor(
private val context: Context, private val controllerFactory: MediaControllerFactory,
private val localMediaManagerFactory: LocalMediaManagerFactory, private val localMediaManagerFactory: LocalMediaManagerFactory,
private val mr2manager: MediaRouter2Manager, private val mr2manager: MediaRouter2Manager,
@Main private val fgExecutor: Executor, @Main private val fgExecutor: Executor,
@@ -71,7 +72,7 @@ class MediaDeviceManager @Inject constructor(
if (entry == null || entry?.token != data.token) { if (entry == null || entry?.token != data.token) {
entry?.stop() entry?.stop()
val controller = data.token?.let { val controller = data.token?.let {
MediaController(context, it) controllerFactory.create(it)
} }
entry = Entry(key, oldKey, controller, entry = Entry(key, oldKey, controller,
localMediaManagerFactory.create(data.packageName)) localMediaManagerFactory.create(data.packageName))
@@ -122,11 +123,12 @@ class MediaDeviceManager @Inject constructor(
val oldKey: String?, val oldKey: String?,
val controller: MediaController?, val controller: MediaController?,
val localMediaManager: LocalMediaManager val localMediaManager: LocalMediaManager
) : LocalMediaManager.DeviceCallback { ) : LocalMediaManager.DeviceCallback, MediaController.Callback() {
val token val token
get() = controller?.sessionToken get() = controller?.sessionToken
private var started = false private var started = false
private var playbackType = PLAYBACK_TYPE_UNKNOWN
private var current: MediaDevice? = null private var current: MediaDevice? = null
set(value) { set(value) {
if (!started || value != field) { if (!started || value != field) {
@@ -141,6 +143,8 @@ class MediaDeviceManager @Inject constructor(
fun start() = bgExecutor.execute { fun start() = bgExecutor.execute {
localMediaManager.registerCallback(this) localMediaManager.registerCallback(this)
localMediaManager.startScan() localMediaManager.startScan()
playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
controller?.registerCallback(this)
updateCurrent() updateCurrent()
started = true started = true
} }
@@ -148,22 +152,37 @@ class MediaDeviceManager @Inject constructor(
@AnyThread @AnyThread
fun stop() = bgExecutor.execute { fun stop() = bgExecutor.execute {
started = false started = false
controller?.unregisterCallback(this)
localMediaManager.stopScan() localMediaManager.stopScan()
localMediaManager.unregisterCallback(this) localMediaManager.unregisterCallback(this)
} }
fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
val route = controller?.let { val routingSession = controller?.let {
mr2manager.getRoutingSessionForMediaController(it) mr2manager.getRoutingSessionForMediaController(it)
} }
val selectedRoutes = routingSession?.let {
mr2manager.getSelectedRoutes(it)
}
with(pw) { with(pw) {
println(" current device is ${current?.name}") println(" current device is ${current?.name}")
val type = controller?.playbackInfo?.playbackType val type = controller?.playbackInfo?.playbackType
println(" PlaybackType=$type (1 for local, 2 for remote)") println(" PlaybackType=$type (1 for local, 2 for remote) cached=$playbackType")
println(" route=$route") println(" routingSession=$routingSession")
println(" selectedRoutes=$selectedRoutes")
} }
} }
@WorkerThread
override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
val newPlaybackType = info?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
if (newPlaybackType == playbackType) {
return
}
playbackType = newPlaybackType
updateCurrent()
}
override fun onDeviceListUpdate(devices: List<MediaDevice>?) = bgExecutor.execute { override fun onDeviceListUpdate(devices: List<MediaDevice>?) = bgExecutor.execute {
updateCurrent() updateCurrent()
} }

View File

@@ -16,13 +16,12 @@
package com.android.systemui.media package com.android.systemui.media
import android.app.Notification
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.media.MediaMetadata
import android.media.MediaRouter2Manager import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo import android.media.RoutingSessionInfo
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import android.testing.TestableLooper import android.testing.TestableLooper
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
@@ -55,7 +54,6 @@ private const val KEY = "TEST_KEY"
private const val KEY_OLD = "TEST_KEY_OLD" private const val KEY_OLD = "TEST_KEY_OLD"
private const val PACKAGE = "PKG" private const val PACKAGE = "PKG"
private const val SESSION_KEY = "SESSION_KEY" private const val SESSION_KEY = "SESSION_KEY"
private const val SESSION_ARTIST = "SESSION_ARTIST"
private const val SESSION_TITLE = "SESSION_TITLE" private const val SESSION_TITLE = "SESSION_TITLE"
private const val DEVICE_NAME = "DEVICE_NAME" private const val DEVICE_NAME = "DEVICE_NAME"
private const val USER_ID = 0 private const val USER_ID = 0
@@ -68,6 +66,7 @@ private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
public class MediaDeviceManagerTest : SysuiTestCase() { public class MediaDeviceManagerTest : SysuiTestCase() {
private lateinit var manager: MediaDeviceManager private lateinit var manager: MediaDeviceManager
@Mock private lateinit var controllerFactory: MediaControllerFactory
@Mock private lateinit var lmmFactory: LocalMediaManagerFactory @Mock private lateinit var lmmFactory: LocalMediaManagerFactory
@Mock private lateinit var lmm: LocalMediaManager @Mock private lateinit var lmm: LocalMediaManager
@Mock private lateinit var mr2: MediaRouter2Manager @Mock private lateinit var mr2: MediaRouter2Manager
@@ -78,10 +77,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Mock private lateinit var device: MediaDevice @Mock private lateinit var device: MediaDevice
@Mock private lateinit var icon: Drawable @Mock private lateinit var icon: Drawable
@Mock private lateinit var route: RoutingSessionInfo @Mock private lateinit var route: RoutingSessionInfo
@Mock private lateinit var controller: MediaController
@Mock private lateinit var playbackInfo: PlaybackInfo
private lateinit var session: MediaSession private lateinit var session: MediaSession
private lateinit var metadataBuilder: MediaMetadata.Builder
private lateinit var playbackBuilder: PlaybackState.Builder
private lateinit var notifBuilder: Notification.Builder
private lateinit var mediaData: MediaData private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule() @JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -89,8 +87,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
fun setUp() { fun setUp() {
fakeFgExecutor = FakeExecutor(FakeSystemClock()) fakeFgExecutor = FakeExecutor(FakeSystemClock())
fakeBgExecutor = FakeExecutor(FakeSystemClock()) fakeBgExecutor = FakeExecutor(FakeSystemClock())
manager = MediaDeviceManager(context, lmmFactory, mr2, fakeFgExecutor, fakeBgExecutor, manager = MediaDeviceManager(controllerFactory, lmmFactory, mr2, fakeFgExecutor,
dumpster) fakeBgExecutor, dumpster)
manager.addListener(listener) manager.addListener(listener)
// Configure mocks. // Configure mocks.
@@ -101,28 +99,13 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(route) whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(route)
// Create a media sesssion and notification for testing. // Create a media sesssion and notification for testing.
metadataBuilder = MediaMetadata.Builder().apply { session = MediaSession(context, SESSION_KEY)
putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
}
playbackBuilder = PlaybackState.Builder().apply {
setState(PlaybackState.STATE_PAUSED, 6000L, 1f)
setActions(PlaybackState.ACTION_PLAY)
}
session = MediaSession(context, SESSION_KEY).apply {
setMetadata(metadataBuilder.build())
setPlaybackState(playbackBuilder.build())
}
session.setActive(true)
notifBuilder = Notification.Builder(context, "NONE").apply {
setContentTitle(SESSION_TITLE)
setContentText(SESSION_ARTIST)
setSmallIcon(android.R.drawable.ic_media_pause)
setStyle(Notification.MediaStyle().setMediaSession(session.getSessionToken()))
}
mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null, mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null,
emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null, emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null,
device = null, active = true, resumeAction = null) device = null, active = true, resumeAction = null)
whenever(controllerFactory.create(session.sessionToken))
.thenReturn(controller)
} }
@After @After
@@ -336,6 +319,41 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
assertThat(data.icon).isNull() assertThat(data.icon).isNull()
} }
@Test
fun audioInfoChanged() {
whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
// GIVEN a controller with local playback type
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady()
reset(mr2)
// WHEN onAudioInfoChanged fires with remote playback type
whenever(playbackInfo.getPlaybackType()).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
verify(mr2).getRoutingSessionForMediaController(eq(controller))
}
@Test
fun audioInfoHasntChanged() {
whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
// GIVEN a controller with remote playback type
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady()
reset(mr2)
// WHEN onAudioInfoChanged fires with remote playback type
val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
verify(controller).registerCallback(captor.capture())
captor.value.onAudioInfoChanged(playbackInfo)
// THEN the route is not checked
verify(mr2, never()).getRoutingSessionForMediaController(eq(controller))
}
fun captureCallback(): LocalMediaManager.DeviceCallback { fun captureCallback(): LocalMediaManager.DeviceCallback {
val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java) val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
verify(lmm).registerCallback(captor.capture()) verify(lmm).registerCallback(captor.capture())