Maybe it's just me but Media3 Create a MediaController
section in android docs is just missing the last part for me which is getting the "player" and using it in a composable.
I want the session for the notification as well and the player will play a url not local media.
I have PlaybackService.kt
which is exactly what's in the documents
import android.content.ComponentName
import android.content.Intent
import androidx.media3.common.MediaMetadata
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.session.MediaController
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService
import androidx.media3.session.SessionToken
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
class PlaybackService : MediaSessionService() {
private lateinit var controllerFuture: ListenableFuture<MediaController>
lateinit var player: MediaController
private var mediaSession: MediaSession? = null
override fun onCreate() {
super.onCreate()
val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
controllerFuture =
MediaController.Builder(this, sessionToken).buildAsync()
controllerFuture.addListener({
player = controllerFuture.get()
initController()
}, MoreExecutors.directExecutor())
}
private fun initController() {
player.addListener(object : Player.Listener {
override fun onMediaMetadataChanged(mediaMetadata: MediaMetadata) {
super.onMediaMetadataChanged(mediaMetadata)
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying)
}
override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState)
}
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
}
override fun onPlayerErrorChanged(error: PlaybackException?) {
super.onPlayerErrorChanged(error)
}
})
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
mediaSession
// The user dismissed the app from the recent tasks
override fun onTaskRemoved(rootIntent: Intent?) {
if (!player.playWhenReady || (player.mediaItemCount == 0)) {
// Stop the service if not playing, continue playing in the background
// otherwise.
stopSelf()
}
}
override fun onDestroy() {
player.stop()
player.release()
super.onDestroy()
}
}
and in AndroidManifest.xml
:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
...
...
<service
android:name=".PlaybackService"
android:exported="true"
android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService" />
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
now, I just need that player variable in a composable.
Maybe something like sending it to HomeScreen(player: MediaController)
I've tried multiple examples from open source projects but all of them are using MVVM with big projects which over overcomplicated.
I'm still learning, I just need to use something like player.play(url: String)
and player.stop()
on a button click and that's it.
I found myself in this same situation about a year ago so I came up with a helper function to deal with it. You can call this with a context. Don't forget to replace the media service class with yours.
It is also worth stating that you should accept the incoming connection from your MediaService class by attaching a callback to your
MediaSession
object and implementing theonConnect
method