Skip to content

Commit 2eb9e83

Browse files
authored
Update MediaRouteControllerDialog (#80)
* Update `MediaRouteControllerDialog` * Update screenshots --------- Co-authored-by: MGaetan89 <[email protected]>
1 parent fe76857 commit 2eb9e83

15 files changed

+175
-110
lines changed

demo/src/main/java/ch/srgssr/androidx/mediarouter/compose/demo/MainViewModel.kt

+16-4
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
6464
init {
6565
MediaRouter.getInstance(application).setMediaSession(mediaSession)
6666

67-
localPlayer.setMediaItem(mediaItem)
67+
localPlayer.setMediaItems(listOf(mediaItemAudio, mediaItemVideo))
6868
localPlayer.volume = 0f
6969
localPlayer.prepare()
7070
localPlayer.play()
@@ -91,8 +91,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
9191
override fun onMediaMetadataChanged(mediaMetadata: MediaMetadata) {
9292
mediaSession.setMetadata(
9393
PlatformMediaMetadata.Builder()
94-
.putText(METADATA_KEY_TITLE, mediaItem.mediaMetadata.title)
95-
.putText(METADATA_KEY_ART_URI, mediaItem.mediaMetadata.artworkUri.toString())
94+
.putText(METADATA_KEY_TITLE, mediaMetadata.title)
95+
.putText(METADATA_KEY_ART_URI, mediaMetadata.artworkUri.toString())
9696
.build()
9797
)
9898
}
@@ -128,7 +128,19 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
128128

129129
private companion object {
130130
@Suppress("MaxLineLength")
131-
private val mediaItem = MediaItem.Builder()
131+
private val mediaItemAudio = MediaItem.Builder()
132+
.setUri("https://rts-aod-dd.akamaized.net/ww/13306839/63cc2653-8305-3894-a448-108810b553ef.mp3")
133+
.setMimeType(MimeTypes.AUDIO_MP4)
134+
.setMediaMetadata(
135+
MediaMetadata.Builder()
136+
.setTitle("On en parle")
137+
.setArtworkUri("https://www.rts.ch/2023/09/28/17/49/11872957.image?w=624&h=351".toUri())
138+
.build()
139+
)
140+
.build()
141+
142+
@Suppress("MaxLineLength")
143+
private val mediaItemVideo = MediaItem.Builder()
132144
.setUri("https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd")
133145
.setMimeType(MimeTypes.APPLICATION_MPD)
134146
.setMediaMetadata(

mediarouter-compose/src/main/java/ch/srgssr/androidx/mediarouter/compose/MediaRouteControllerDialog.kt

+12-16
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.padding
1515
import androidx.compose.foundation.lazy.LazyColumn
1616
import androidx.compose.foundation.lazy.items
1717
import androidx.compose.material3.AlertDialog
18-
import androidx.compose.material3.HorizontalDivider
1918
import androidx.compose.material3.Icon
2019
import androidx.compose.material3.IconButton
2120
import androidx.compose.material3.MaterialTheme
@@ -241,10 +240,6 @@ private fun ControllerDialogContent(
241240
)
242241
}
243242

244-
if (showPlaybackControl && showVolumeControl) {
245-
HorizontalDivider(modifier = Modifier.padding(vertical = 8.dp))
246-
}
247-
248243
if (showVolumeControl) {
249244
VolumeControl(
250245
route = route,
@@ -313,6 +308,7 @@ private fun PlaybackControlRow(
313308
Column(
314309
modifier = Modifier
315310
.weight(1f)
311+
.padding(vertical = 8.dp)
316312
.clickable(onClick = onTitleClick),
317313
) {
318314
if (title != null) {
@@ -355,7 +351,7 @@ private fun VolumeControl(
355351
horizontalArrangement = Arrangement.spacedBy(8.dp),
356352
verticalAlignment = Alignment.CenterVertically,
357353
) {
358-
var volume by remember { mutableFloatStateOf(route.volume.toFloat()) }
354+
val (volume, setVolume) = remember { mutableFloatStateOf(route.volume.toFloat()) }
359355

360356
Icon(
361357
imageVector = Icons.Audiotrack,
@@ -364,13 +360,10 @@ private fun VolumeControl(
364360

365361
Slider(
366362
value = volume,
367-
onValueChange = {
368-
volume = it
369-
370-
route.requestSetVolume(it.toInt())
371-
},
363+
onValueChange = setVolume,
372364
modifier = Modifier.weight(1f),
373365
valueRange = 0f..route.volumeMax.toFloat(),
366+
onValueChangeFinished = { route.requestSetVolume(volume.toInt()) },
374367
)
375368

376369
if (route.isGroup && route.memberRoutes.size > 1) {
@@ -405,8 +398,8 @@ private fun DeviceGroup(
405398
items(routes) { route ->
406399
val isVolumeControlEnabled = volumeControlEnabled
407400
&& route.volumeHandling == RouteInfo.PLAYBACK_VOLUME_VARIABLE
408-
val volumeRange = 0f..route.volumeMax.toFloat()
409-
val enabled = route.isEnabled
401+
val volumeMax = if (isVolumeControlEnabled) route.volumeMax else 100
402+
val volumeRange = 0f..volumeMax.toFloat()
410403

411404
var volume by remember {
412405
mutableFloatStateOf(if (isVolumeControlEnabled) route.volume.toFloat() else 100f)
@@ -436,13 +429,16 @@ private fun DeviceGroup(
436429
onValueChange = {
437430
if (isVolumeControlEnabled) {
438431
volume = it
439-
440-
route.requestSetVolume(it.toInt())
441432
}
442433
},
443434
modifier = Modifier.weight(1f),
444-
enabled = enabled,
435+
enabled = route.isEnabled,
445436
valueRange = volumeRange,
437+
onValueChangeFinished = {
438+
if (isVolumeControlEnabled) {
439+
route.requestSetVolume(volume.toInt())
440+
}
441+
},
446442
)
447443
}
448444
}

mediarouter-compose/src/main/java/ch/srgssr/androidx/mediarouter/compose/MediaRouteControllerDialogViewModel.kt

+38-26
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,17 @@ package ch.srgssr.androidx.mediarouter.compose
77

88
import android.app.Application
99
import android.app.PendingIntent
10-
import android.media.MediaDescription
11-
import android.media.MediaMetadata
12-
import android.media.session.MediaController
13-
import android.media.session.MediaSession
14-
import android.media.session.PlaybackState
15-
import android.media.session.PlaybackState.ACTION_PAUSE
16-
import android.media.session.PlaybackState.ACTION_PLAY
17-
import android.media.session.PlaybackState.ACTION_PLAY_PAUSE
18-
import android.media.session.PlaybackState.ACTION_STOP
19-
import android.media.session.PlaybackState.STATE_BUFFERING
20-
import android.media.session.PlaybackState.STATE_NONE
21-
import android.media.session.PlaybackState.STATE_PLAYING
10+
import android.support.v4.media.MediaDescriptionCompat
11+
import android.support.v4.media.MediaMetadataCompat
12+
import android.support.v4.media.session.MediaControllerCompat
13+
import android.support.v4.media.session.PlaybackStateCompat
14+
import android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE
15+
import android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY
16+
import android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE
17+
import android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP
18+
import android.support.v4.media.session.PlaybackStateCompat.STATE_BUFFERING
19+
import android.support.v4.media.session.PlaybackStateCompat.STATE_NONE
20+
import android.support.v4.media.session.PlaybackStateCompat.STATE_PLAYING
2221
import android.util.Log
2322
import androidx.annotation.VisibleForTesting
2423
import androidx.compose.ui.graphics.vector.ImageVector
@@ -47,13 +46,14 @@ import kotlinx.coroutines.flow.combine
4746
import kotlinx.coroutines.flow.map
4847
import kotlinx.coroutines.flow.stateIn
4948
import kotlinx.coroutines.flow.update
49+
import kotlinx.coroutines.launch
5050

5151
internal class MediaRouteControllerDialogViewModel(
5252
private val application: Application,
5353
private val savedStateHandle: SavedStateHandle,
5454
private val volumeControlEnabled: Boolean,
5555
) : AndroidViewModel(application) {
56-
private var mediaController: MediaController? = null
56+
private var mediaController: MediaControllerCompat? = null
5757

5858
private val mediaControllerCallback = MediaControllerCallback()
5959
private val mediaRouterCallback = MediaRouterCallback()
@@ -63,10 +63,10 @@ internal class MediaRouteControllerDialogViewModel(
6363
private val routerUpdates = MutableStateFlow(0)
6464

6565
@VisibleForTesting
66-
internal val mediaDescription = MutableStateFlow<MediaDescription?>(null)
66+
internal val mediaDescription = MutableStateFlow<MediaDescriptionCompat?>(null)
6767

6868
@VisibleForTesting
69-
internal val playbackState = MutableStateFlow<PlaybackState?>(null)
69+
internal val playbackState = MutableStateFlow<PlaybackStateCompat?>(null)
7070
private val _isDeviceGroupExpanded = MutableStateFlow(false)
7171
private val _selectedRoute = routerUpdates.map { router.selectedRoute }
7272

@@ -80,9 +80,14 @@ internal class MediaRouteControllerDialogViewModel(
8080
mediaDescription != null || playbackState != null
8181
}.stateIn(viewModelScope, WhileSubscribed(), false)
8282
val showVolumeControl = _selectedRoute.map { selectedRoute ->
83-
selectedRoute.volumeHandling == RouteInfo.PLAYBACK_VOLUME_VARIABLE
84-
&& (isGroupVolumeUxEnabled || !(selectedRoute.isGroup && selectedRoute.memberRoutes.size > 1))
85-
&& volumeControlEnabled
83+
if (!isGroupVolumeUxEnabled && selectedRoute.isGroup && selectedRoute.memberRoutes.size > 1) {
84+
_isDeviceGroupExpanded.update { true }
85+
false
86+
} else {
87+
selectedRoute.volumeHandling == RouteInfo.PLAYBACK_VOLUME_VARIABLE
88+
&& (!_isDeviceGroupExpanded.value || isGroupVolumeUxEnabled)
89+
&& volumeControlEnabled
90+
}
8691
}.stateIn(viewModelScope, WhileSubscribed(), false)
8792
val imageModel = mediaDescription.map { mediaDescription ->
8893
mediaDescription?.iconBitmap?.takeIf { !it.isRecycled } ?: mediaDescription?.iconUri
@@ -139,14 +144,21 @@ internal class MediaRouteControllerDialogViewModel(
139144
)
140145

141146
router.mediaSessionToken?.let { mediaSessionToken ->
142-
val platformToken = mediaSessionToken.token as MediaSession.Token
143-
val mediaController = MediaController(application, platformToken)
147+
val mediaController = MediaControllerCompat(application, mediaSessionToken)
144148
mediaController.registerCallback(mediaControllerCallback)
145149

146150
this.mediaController = mediaController
147151
mediaDescription.update { mediaController.metadata?.description }
148152
playbackState.update { mediaController.playbackState }
149153
}
154+
155+
viewModelScope.launch {
156+
_selectedRoute.collect {
157+
if (!it.isSelected || it.isDefaultOrBluetooth) {
158+
hideDialog()
159+
}
160+
}
161+
}
150162
}
151163

152164
fun hideDialog() {
@@ -222,13 +234,13 @@ internal class MediaRouteControllerDialogViewModel(
222234
mediaControllerCallback.onSessionDestroyed()
223235
}
224236

225-
private val PlaybackState.isPlayActionSupported: Boolean
237+
private val PlaybackStateCompat.isPlayActionSupported: Boolean
226238
get() = actions and (ACTION_PLAY or ACTION_PLAY_PAUSE) != 0L
227239

228-
private val PlaybackState.isPauseActionSupported: Boolean
240+
private val PlaybackStateCompat.isPauseActionSupported: Boolean
229241
get() = actions and (ACTION_PAUSE or ACTION_PLAY_PAUSE) != 0L
230242

231-
private val PlaybackState.isStopActionSupported: Boolean
243+
private val PlaybackStateCompat.isStopActionSupported: Boolean
232244
get() = actions and ACTION_STOP != 0L
233245

234246
private companion object {
@@ -249,18 +261,18 @@ internal class MediaRouteControllerDialogViewModel(
249261
}
250262
}
251263

252-
private inner class MediaControllerCallback : MediaController.Callback() {
264+
private inner class MediaControllerCallback : MediaControllerCompat.Callback() {
253265
override fun onSessionDestroyed() {
254266
mediaController?.unregisterCallback(this)
255267
mediaController = null
256268
}
257269

258-
override fun onPlaybackStateChanged(state: PlaybackState?) {
270+
override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {
259271
playbackState.update { state }
260272
routerUpdates.update { it + 1 }
261273
}
262274

263-
override fun onMetadataChanged(metadata: MediaMetadata?) {
275+
override fun onMetadataChanged(metadata: MediaMetadataCompat?) {
264276
mediaDescription.update { metadata?.description }
265277
routerUpdates.update { it + 1 }
266278
}

0 commit comments

Comments
 (0)