I have the following code which I use with media3 ...
# compose
z
I have the following code which I use with media3 for playing videos. The thing I'm wondering is is how can I set it up to display the various states of the player? Such as it being paused, buffering, the elapsed time, etc. I'm not sure if I should use a LaunchedEffect and add the listener there, or if a viewmodel would be better, or maybe even some other way.
Copy code
@Composable
fun Player(
    modifier: Modifier = Modifier,
    player: Player
) {
    AndroidView(
        modifier = Modifier
            .aspectRatio(16f / 9f)
            .then(modifier),
        factory = { context ->
            SurfaceView(context)
        },
        update = { surfaceView ->
            player.setVideoSurfaceView(surfaceView)
        }
    )
}
p
I would do something like bellow:
Copy code
@Composable
fun Player(
    modifier: Modifier = Modifier,
    playerState: IPlayerState
) {

    val mediaState = playerState.mediaFlow.collectAsStateWithLifecycle(MediaState.Initializing)
    
    AndroidView(
        modifier = Modifier
            .aspectRatio(16f / 9f)
            .then(modifier),
        factory = { context ->
            SurfaceView(context)
        },
        update = { surfaceView ->

            when (mediaState) {
                 Initializing -> {} // Ignore initial state, wait for State to be ready
                 Streaming -> {
                      playerState.player.setVideoSurfaceView(surfaceView)
                  }
                  Paused -> {
                      // Show pause UI
                  }
         
        }
    )
    
}

class PlayerState(player: PlayerController) : IPlayerState {
    val mediaFlow = StateFlow<MediaState>

    init() {
        player.startStreaming()
        mediaFlow.value = MediaState.Streaming
    }

    fun stopVideo() {
        player.stopStreaming()
        mediaFlow.value = MediaState.Paused
    }
}

sealed interface MediaState {
    object Initializing: MediaState
    object Streaming: MediaState
    object Paused: MediaState
    ...
}
I am assuming a hypothetical player controller class api. My point is to try to pass the State of your composable as an input parameter rather than using remember. The reason is being able to inject it later for test-ability.
y
We've tackled this extensively for Audio on Wear in the Horologist project. But for player state it shows an approach for similar requirements.
Some key lessons. Definitely use a viewmodel, for lifecycle reasons. But it should defer logic to some reusable controller/state pair.
Aim to keep the UI simple, so model co plexities of state (loading, buffering, playwhen ready) outside composables.