Loading packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +6 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.hardware.SensorManager; import android.hardware.SensorPrivacyManager; import android.hardware.display.DisplayManager; import android.media.AudioManager; import android.media.MediaRouter2Manager; import android.net.ConnectivityManager; import android.net.NetworkScoreManager; import android.net.wifi.WifiManager; Loading Loading @@ -211,6 +212,11 @@ public class SystemServicesModule { return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL); } @Provides static MediaRouter2Manager provideMediaRouter2Manager(Context context) { return MediaRouter2Manager.getInstance(context); } @Provides @Singleton static NetworkScoreManager provideNetworkScoreManager(Context context) { Loading packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +5 −14 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.Icon; import android.graphics.drawable.RippleDrawable; import android.media.session.MediaController; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.service.media.MediaBrowserService; Loading Loading @@ -294,14 +293,6 @@ public class MediaControlPanel { mActivityStarter.startActivity(intent, false, true /* dismissShade */, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); }); final boolean isRemotePlayback; PlaybackInfo playbackInfo = mController.getPlaybackInfo(); if (playbackInfo != null) { isRemotePlayback = playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; } else { Log.d(TAG, "PlaybackInfo was null. Defaulting to local playback."); isRemotePlayback = false; } ImageView iconView = mViewHolder.getSeamlessIcon(); TextView deviceName = mViewHolder.getSeamlessText(); Loading @@ -312,18 +303,18 @@ public class MediaControlPanel { rect.setStroke(2, deviceName.getCurrentTextColor()); rect.setColor(Color.TRANSPARENT); if (isRemotePlayback) { final MediaDeviceData device = data.getDevice(); if (device != null && !device.getEnabled()) { mViewHolder.getSeamless().setEnabled(false); // TODO(b/156875717): setEnabled should cause the alpha to change. mViewHolder.getSeamless().setAlpha(0.38f); iconView.setImageResource(R.drawable.ic_hardware_speaker); iconView.setVisibility(View.VISIBLE); deviceName.setText(R.string.media_seamless_remote_device); } else if (data.getDevice() != null && data.getDevice().getIcon() != null && data.getDevice().getName() != null) { } else if (device != null) { mViewHolder.getSeamless().setEnabled(true); mViewHolder.getSeamless().setAlpha(1f); Drawable icon = data.getDevice().getIcon(); Drawable icon = device.getIcon(); iconView.setVisibility(View.VISIBLE); if (icon instanceof AdaptiveIcon) { Loading @@ -333,7 +324,7 @@ public class MediaControlPanel { } else { iconView.setImageDrawable(icon); } deviceName.setText(data.getDevice().getName()); deviceName.setText(device.getName()); } else { // Reset to default Log.w(TAG, "device is null. Not binding output chip."); Loading packages/SystemUI/src/com/android/systemui/media/MediaData.kt +1 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ data class MediaAction( /** State of the media device. */ data class MediaDeviceData( val enabled: Boolean, val icon: Drawable?, val name: String? ) packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +26 −6 Original line number Diff line number Diff line Loading @@ -16,8 +16,12 @@ package com.android.systemui.media import android.app.Notification import android.content.Context import android.service.notification.StatusBarNotification import android.media.MediaRouter2Manager import android.media.session.MediaSession import android.media.session.MediaController import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice import com.android.systemui.dagger.qualifiers.Main Loading @@ -32,6 +36,7 @@ import javax.inject.Singleton class MediaDeviceManager @Inject constructor( private val context: Context, private val localMediaManagerFactory: LocalMediaManagerFactory, private val mr2manager: MediaRouter2Manager, private val featureFlag: MediaFeatureFlag, @Main private val fgExecutor: Executor ) { Loading @@ -52,7 +57,10 @@ class MediaDeviceManager @Inject constructor( if (featureFlag.enabled && isMediaNotification(sbn)) { var tok = entries[key] if (tok == null) { tok = Token(key, localMediaManagerFactory.create(sbn.packageName)) val token = sbn.notification.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) as MediaSession.Token? val controller = MediaController(context, token) tok = Token(key, controller, localMediaManagerFactory.create(sbn.packageName)) entries[key] = tok tok.start() } Loading @@ -72,7 +80,8 @@ class MediaDeviceManager @Inject constructor( } private fun processDevice(key: String, device: MediaDevice?) { val data = MediaDeviceData(device?.icon, device?.name) val enabled = device != null val data = MediaDeviceData(enabled, device?.icon, device?.name) listeners.forEach { it.onMediaDeviceChanged(key, data) } Loading @@ -87,11 +96,13 @@ class MediaDeviceManager @Inject constructor( private inner class Token( val key: String, val controller: MediaController, val localMediaManager: LocalMediaManager ) : LocalMediaManager.DeviceCallback { private var started = false private var current: MediaDevice? = null set(value) { if (value != field) { if (!started || value != field) { field = value processDevice(key, value) } Loading @@ -99,19 +110,28 @@ class MediaDeviceManager @Inject constructor( fun start() { localMediaManager.registerCallback(this) localMediaManager.startScan() current = localMediaManager.getCurrentConnectedDevice() updateCurrent() started = true } fun stop() { started = false localMediaManager.stopScan() localMediaManager.unregisterCallback(this) } override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute { current = localMediaManager.getCurrentConnectedDevice() updateCurrent() } override fun onSelectedDeviceStateChanged(device: MediaDevice, state: Int) { fgExecutor.execute { current = device updateCurrent() } } private fun updateCurrent() { val device = localMediaManager.getCurrentConnectedDevice() val route = mr2manager.getRoutingSessionForMediaController(controller) // If we get a null route, then don't trust the device. Just set to null to disable the // output switcher chip. current = if (route != null) device else null } } } packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +37 −5 Original line number Diff line number Diff line Loading @@ -98,6 +98,8 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var action4: ImageButton private lateinit var session: MediaSession private val device = MediaDeviceData(true, null, DEVICE_NAME) private val disabledDevice = MediaDeviceData(false, null, null) @Before fun setUp() { Loading Loading @@ -181,7 +183,7 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindWhenUnattached() { val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, null, null, MediaDeviceData(null, DEVICE_NAME)) emptyList(), PACKAGE, null, null, device) player.bind(state) assertThat(player.isPlaying()).isFalse() } Loading @@ -190,8 +192,7 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindText() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, MediaDeviceData(null, DEVICE_NAME)) emptyList(), PACKAGE, session.getSessionToken(), null, device) player.bind(state) assertThat(appName.getText()).isEqualTo(APP) assertThat(titleText.getText()).isEqualTo(TITLE) Loading @@ -202,9 +203,40 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindBackgroundColor() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, MediaDeviceData(null, DEVICE_NAME)) emptyList(), PACKAGE, session.getSessionToken(), null, device) player.bind(state) assertThat(background.getBackgroundTintList()).isEqualTo(ColorStateList.valueOf(BG_COLOR)) } @Test fun bindDevice() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, device) player.bind(state) assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME) assertThat(seamless.isEnabled()).isTrue() } @Test fun bindDisabledDevice() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice) player.bind(state) assertThat(seamless.isEnabled()).isFalse() assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString( R.string.media_seamless_remote_device)) } @Test fun bindNullDevice() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, null) player.bind(state) assertThat(seamless.isEnabled()).isTrue() assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString( com.android.internal.R.string.ext_media_seamless_action)) } } Loading
packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +6 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.hardware.SensorManager; import android.hardware.SensorPrivacyManager; import android.hardware.display.DisplayManager; import android.media.AudioManager; import android.media.MediaRouter2Manager; import android.net.ConnectivityManager; import android.net.NetworkScoreManager; import android.net.wifi.WifiManager; Loading Loading @@ -211,6 +212,11 @@ public class SystemServicesModule { return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL); } @Provides static MediaRouter2Manager provideMediaRouter2Manager(Context context) { return MediaRouter2Manager.getInstance(context); } @Provides @Singleton static NetworkScoreManager provideNetworkScoreManager(Context context) { Loading
packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +5 −14 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.Icon; import android.graphics.drawable.RippleDrawable; import android.media.session.MediaController; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.service.media.MediaBrowserService; Loading Loading @@ -294,14 +293,6 @@ public class MediaControlPanel { mActivityStarter.startActivity(intent, false, true /* dismissShade */, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); }); final boolean isRemotePlayback; PlaybackInfo playbackInfo = mController.getPlaybackInfo(); if (playbackInfo != null) { isRemotePlayback = playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; } else { Log.d(TAG, "PlaybackInfo was null. Defaulting to local playback."); isRemotePlayback = false; } ImageView iconView = mViewHolder.getSeamlessIcon(); TextView deviceName = mViewHolder.getSeamlessText(); Loading @@ -312,18 +303,18 @@ public class MediaControlPanel { rect.setStroke(2, deviceName.getCurrentTextColor()); rect.setColor(Color.TRANSPARENT); if (isRemotePlayback) { final MediaDeviceData device = data.getDevice(); if (device != null && !device.getEnabled()) { mViewHolder.getSeamless().setEnabled(false); // TODO(b/156875717): setEnabled should cause the alpha to change. mViewHolder.getSeamless().setAlpha(0.38f); iconView.setImageResource(R.drawable.ic_hardware_speaker); iconView.setVisibility(View.VISIBLE); deviceName.setText(R.string.media_seamless_remote_device); } else if (data.getDevice() != null && data.getDevice().getIcon() != null && data.getDevice().getName() != null) { } else if (device != null) { mViewHolder.getSeamless().setEnabled(true); mViewHolder.getSeamless().setAlpha(1f); Drawable icon = data.getDevice().getIcon(); Drawable icon = device.getIcon(); iconView.setVisibility(View.VISIBLE); if (icon instanceof AdaptiveIcon) { Loading @@ -333,7 +324,7 @@ public class MediaControlPanel { } else { iconView.setImageDrawable(icon); } deviceName.setText(data.getDevice().getName()); deviceName.setText(device.getName()); } else { // Reset to default Log.w(TAG, "device is null. Not binding output chip."); Loading
packages/SystemUI/src/com/android/systemui/media/MediaData.kt +1 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ data class MediaAction( /** State of the media device. */ data class MediaDeviceData( val enabled: Boolean, val icon: Drawable?, val name: String? )
packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +26 −6 Original line number Diff line number Diff line Loading @@ -16,8 +16,12 @@ package com.android.systemui.media import android.app.Notification import android.content.Context import android.service.notification.StatusBarNotification import android.media.MediaRouter2Manager import android.media.session.MediaSession import android.media.session.MediaController import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice import com.android.systemui.dagger.qualifiers.Main Loading @@ -32,6 +36,7 @@ import javax.inject.Singleton class MediaDeviceManager @Inject constructor( private val context: Context, private val localMediaManagerFactory: LocalMediaManagerFactory, private val mr2manager: MediaRouter2Manager, private val featureFlag: MediaFeatureFlag, @Main private val fgExecutor: Executor ) { Loading @@ -52,7 +57,10 @@ class MediaDeviceManager @Inject constructor( if (featureFlag.enabled && isMediaNotification(sbn)) { var tok = entries[key] if (tok == null) { tok = Token(key, localMediaManagerFactory.create(sbn.packageName)) val token = sbn.notification.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) as MediaSession.Token? val controller = MediaController(context, token) tok = Token(key, controller, localMediaManagerFactory.create(sbn.packageName)) entries[key] = tok tok.start() } Loading @@ -72,7 +80,8 @@ class MediaDeviceManager @Inject constructor( } private fun processDevice(key: String, device: MediaDevice?) { val data = MediaDeviceData(device?.icon, device?.name) val enabled = device != null val data = MediaDeviceData(enabled, device?.icon, device?.name) listeners.forEach { it.onMediaDeviceChanged(key, data) } Loading @@ -87,11 +96,13 @@ class MediaDeviceManager @Inject constructor( private inner class Token( val key: String, val controller: MediaController, val localMediaManager: LocalMediaManager ) : LocalMediaManager.DeviceCallback { private var started = false private var current: MediaDevice? = null set(value) { if (value != field) { if (!started || value != field) { field = value processDevice(key, value) } Loading @@ -99,19 +110,28 @@ class MediaDeviceManager @Inject constructor( fun start() { localMediaManager.registerCallback(this) localMediaManager.startScan() current = localMediaManager.getCurrentConnectedDevice() updateCurrent() started = true } fun stop() { started = false localMediaManager.stopScan() localMediaManager.unregisterCallback(this) } override fun onDeviceListUpdate(devices: List<MediaDevice>?) = fgExecutor.execute { current = localMediaManager.getCurrentConnectedDevice() updateCurrent() } override fun onSelectedDeviceStateChanged(device: MediaDevice, state: Int) { fgExecutor.execute { current = device updateCurrent() } } private fun updateCurrent() { val device = localMediaManager.getCurrentConnectedDevice() val route = mr2manager.getRoutingSessionForMediaController(controller) // If we get a null route, then don't trust the device. Just set to null to disable the // output switcher chip. current = if (route != null) device else null } } }
packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +37 −5 Original line number Diff line number Diff line Loading @@ -98,6 +98,8 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var action4: ImageButton private lateinit var session: MediaSession private val device = MediaDeviceData(true, null, DEVICE_NAME) private val disabledDevice = MediaDeviceData(false, null, null) @Before fun setUp() { Loading Loading @@ -181,7 +183,7 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindWhenUnattached() { val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, null, null, MediaDeviceData(null, DEVICE_NAME)) emptyList(), PACKAGE, null, null, device) player.bind(state) assertThat(player.isPlaying()).isFalse() } Loading @@ -190,8 +192,7 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindText() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, MediaDeviceData(null, DEVICE_NAME)) emptyList(), PACKAGE, session.getSessionToken(), null, device) player.bind(state) assertThat(appName.getText()).isEqualTo(APP) assertThat(titleText.getText()).isEqualTo(TITLE) Loading @@ -202,9 +203,40 @@ public class MediaControlPanelTest : SysuiTestCase() { fun bindBackgroundColor() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, MediaDeviceData(null, DEVICE_NAME)) emptyList(), PACKAGE, session.getSessionToken(), null, device) player.bind(state) assertThat(background.getBackgroundTintList()).isEqualTo(ColorStateList.valueOf(BG_COLOR)) } @Test fun bindDevice() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, device) player.bind(state) assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME) assertThat(seamless.isEnabled()).isTrue() } @Test fun bindDisabledDevice() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice) player.bind(state) assertThat(seamless.isEnabled()).isFalse() assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString( R.string.media_seamless_remote_device)) } @Test fun bindNullDevice() { player.attach(holder) val state = MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), emptyList(), PACKAGE, session.getSessionToken(), null, null) player.bind(state) assertThat(seamless.isEnabled()).isTrue() assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString( com.android.internal.R.string.ext_media_seamless_action)) } }