66package ch.srgssr.androidx.mediarouter.compose.demo
77
88import android.app.Application
9+ import android.media.MediaMetadata.METADATA_KEY_ART_URI
10+ import android.media.MediaMetadata.METADATA_KEY_TITLE
11+ import android.media.session.MediaSession
12+ import android.media.session.PlaybackState
913import androidx.annotation.OptIn
1014import androidx.core.net.toUri
1115import androidx.lifecycle.AndroidViewModel
@@ -15,18 +19,23 @@ import androidx.media3.cast.SessionAvailabilityListener
1519import androidx.media3.common.MediaItem
1620import androidx.media3.common.MediaMetadata
1721import androidx.media3.common.MimeTypes
22+ import androidx.media3.common.Player
1823import androidx.media3.common.util.UnstableApi
1924import androidx.media3.exoplayer.ExoPlayer
25+ import androidx.mediarouter.media.MediaRouter
2026import com.google.android.gms.cast.framework.CastContext
2127import kotlinx.coroutines.flow.MutableStateFlow
2228import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
2329import kotlinx.coroutines.flow.onEach
2430import kotlinx.coroutines.flow.stateIn
2531import kotlinx.coroutines.flow.update
2632import kotlin.time.Duration.Companion.seconds
33+ import android.media.MediaMetadata as PlatformMediaMetadata
2734
2835@OptIn(UnstableApi ::class )
2936class MainViewModel (application : Application ) : AndroidViewModel(application) {
37+ private val playerListener = PlayerListener ()
38+ private val mediaSession = MediaSession (application, " androidx-mediarouter-compose-demo" )
3039 private val localPlayer = ExoPlayer .Builder (application).build()
3140 private val castPlayer = CastPlayer (CastContext .getSharedInstance(application))
3241 private val currentPlayer =
@@ -35,13 +44,15 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
3544 val player = currentPlayer
3645 .onEach { player ->
3746 val oldPlayer = if (player == localPlayer) castPlayer else localPlayer
47+ player.addListener(playerListener)
3848 player.volume = oldPlayer.volume
3949 player.repeatMode = oldPlayer.repeatMode
4050 player.playWhenReady = oldPlayer.playWhenReady
4151
4252 oldPlayer.currentMediaItem?.let {
4353 player.setMediaItem(it, oldPlayer.currentPosition)
4454 }
55+ oldPlayer.removeListener(playerListener)
4556 oldPlayer.stop()
4657 }
4758 .stateIn(
@@ -51,6 +62,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
5162 )
5263
5364 init {
65+ MediaRouter .getInstance(application).setMediaSession(mediaSession)
66+
5467 localPlayer.setMediaItem(mediaItem)
5568 localPlayer.volume = 0f
5669 localPlayer.prepare()
@@ -68,11 +81,51 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
6881 }
6982
7083 override fun onCleared () {
84+ mediaSession.release()
7185 localPlayer.release()
7286 castPlayer.setSessionAvailabilityListener(null )
7387 castPlayer.release()
7488 }
7589
90+ private inner class PlayerListener : Player .Listener {
91+ override fun onMediaMetadataChanged (mediaMetadata : MediaMetadata ) {
92+ mediaSession.setMetadata(
93+ PlatformMediaMetadata .Builder ()
94+ .putText(METADATA_KEY_TITLE , mediaItem.mediaMetadata.title)
95+ .putText(METADATA_KEY_ART_URI , mediaItem.mediaMetadata.artworkUri.toString())
96+ .build()
97+ )
98+ }
99+
100+ override fun onPlaybackStateChanged (@Player.State playbackState : Int ) {
101+ val state = when (playbackState) {
102+ Player .STATE_IDLE -> PlaybackState .STATE_CONNECTING
103+ Player .STATE_BUFFERING -> PlaybackState .STATE_BUFFERING
104+ Player .STATE_READY -> PlaybackState .STATE_PLAYING
105+ Player .STATE_ENDED -> PlaybackState .STATE_STOPPED
106+ else -> PlaybackState .STATE_NONE
107+ }
108+ val player = currentPlayer.value
109+ val position = player.currentPosition
110+ val playbackSpeed = player.playbackParameters.speed
111+ val actions = listOfNotNull(
112+ PlaybackState .ACTION_PAUSE .takeIf { player.isCommandAvailable(Player .COMMAND_PLAY_PAUSE ) },
113+ PlaybackState .ACTION_PLAY .takeIf { player.isCommandAvailable(Player .COMMAND_PLAY_PAUSE ) },
114+ PlaybackState .ACTION_PLAY_PAUSE .takeIf { player.isCommandAvailable(Player .COMMAND_PLAY_PAUSE ) },
115+ PlaybackState .ACTION_STOP .takeIf { player.isCommandAvailable(Player .COMMAND_STOP ) },
116+ ).fold(0L ) { actions, action ->
117+ actions or action
118+ }
119+
120+ mediaSession.setPlaybackState(
121+ PlaybackState .Builder ()
122+ .setState(state, position, playbackSpeed)
123+ .setActions(actions)
124+ .build()
125+ )
126+ }
127+ }
128+
76129 private companion object {
77130 @Suppress(" MaxLineLength" )
78131 private val mediaItem = MediaItem .Builder ()
0 commit comments