Skip to content

Instantly share code, notes, and snippets.

@reactormonk
Created September 20, 2023 16:14
Show Gist options
  • Select an option

  • Save reactormonk/563a13bcb00f16971ce1145907e98553 to your computer and use it in GitHub Desktop.

Select an option

Save reactormonk/563a13bcb00f16971ce1145907e98553 to your computer and use it in GitHub Desktop.
@Composable
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
fun AutoPlayPlaylist(mediaItems: List<MediaItem>, mediaItemIndex: Int, position: Long, storeState: (PlayerState) -> (Unit)) {
val TAG = "AutoPlayPlaylist"
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
Log.d(TAG, "MediaItems: $mediaItems")
var done by remember(mediaItems) { Log.d(TAG, "Recreating done."); mutableStateOf(false) }
val exoPlayer = remember(context) {
var started = false
val listener = object : Player.Listener {
override fun onEvents(player: Player, events: Player.Events) {
if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
if (player.playbackState == Player.STATE_ENDED && started) {
done = true
Log.d(TAG, "Done.")
}
}
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
if (isPlaying) {
started = true
}
}
}
val mediaSourceFactory = DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(
DefaultLoadErrorHandlingPolicy()
)
val player = ExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build()
player.addListener(listener)
// player.addAnalyticsListener(EventLogger())
player.repeatMode = REPEAT_MODE_OFF
player
}
fun state(): PlayerState = PlayerState(exoPlayer.currentMediaItem, exoPlayer.currentPosition, done)
storeState(state()) // passing through here on recomposition
Log.i(TAG, "Exo player: $exoPlayer")
LaunchedEffect(mediaItems) {
if (exoPlayer.availableCommands.contains(Player.COMMAND_CHANGE_MEDIA_ITEMS)) {
exoPlayer.setMediaItems(mediaItems)
exoPlayer.prepare()
exoPlayer.playWhenReady = true
} else {
Log.e(TAG, "Can't change media items!?!")
}
}
LaunchedEffect(mediaItemIndex, position) {
if (mediaItemIndex > -1) {
exoPlayer.seekTo(mediaItemIndex, position)
}
}
Log.d(TAG, "currentMediaItem: ${exoPlayer.currentMediaItem}")
DisposableEffect(exoPlayer, mediaItems) {
val lifecycleObserver = LifecycleEventObserver { _, event ->
// TODO sort out the done logic into a separate method and test it on all relevant timings
when (event) {
Lifecycle.Event.ON_STOP -> {
storeState(state())
}
else -> {}
}
}
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
}
}
DisposableEffect(exoPlayer) {
val lifecycleObserver = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_START -> exoPlayer.play()
Lifecycle.Event.ON_STOP -> exoPlayer.pause()
Lifecycle.Event.ON_DESTROY -> exoPlayer.release()
else -> {}
}
}
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
exoPlayer.release()
}
}
Log.d(TAG, "Done: $done")
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
if (!done) {
VideoPlayer(exoPlayer)
} else {
ErrorTemplate(message = stringResource(R.string.end_of_playlist))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment