androidkotlinmvvmandroid-jetpack-composeandroid-mediaplayer

How can I retrieve the current duration from the Media Player to display on the song progress bar?


I have tried every possible step but only giving value once, I want it should update every single second. This is my code, what am I doing wrong?

I want to implement progress slider for musics, can anyone please help me?

I'm using a slider library which you this library accept range from current position to total duration.

data class MusicPlayerStates(
    var playingSongCurrentPosition: MutableState<Int> = mutableIntStateOf(0),
    var playingSongDuration: MutableState<Int> = mutableIntStateOf(0),
    
    //other code
)

fun onEvent(event: MusicPlayerUiEvents) {
        when (event) {

            is MusicPlayerUiEvents.PlaySong -> {
                mediaPlayer?.let {
                    if (it.isPlaying) {
                        mediaPlayer?.stop()
                        mediaPlayer?.reset()
                        _musicPlayerState.update { state ->
                            state.copy(
                                playingSongCurrentPosition = state.playingSongCurrentPosition.apply {
                                    this.value = 0
                                },
                                playingSongDuration = state.playingSongDuration.apply {
                                    this.value = 0
                                }
                            )
                        }
                    }
                }
                _musicPlayerState.update {
                    it.copy(
                        isSongPlaying = it.isSongPlaying.apply {
                            this.value = true
                        }
                    )
                }
                mediaPlayer?.release()
                mediaPlayer = MediaPlayer().apply {
                    setDataSource(event.url)
                    prepareAsync()
                }
                mediaPlayer?.setOnPreparedListener { mediaPlayer ->
                    mediaPlayer.seekTo(state.value.playingSongCurrentPosition.value)
                    mediaPlayer.start()
                    setSongDuration(mediaPlayer.duration)
                    updatePlaybackState(mediaPlayer.currentPosition)
                    
               Log.d("check for currentD_VM","${state.value.playingSongCurrentPosition.value}")
                }

                mediaPlayer?.setOnCompletionListener { mediaPlayer ->
                    // Use for precise updates
                    mediaPlayer?.stop()
                    _musicPlayerState.update { state ->
                        state.copy(
                            playingSongCurrentPosition = state.playingSongCurrentPosition.apply {
                                this.value = 0
                            },
                            playingSongDuration = state.playingSongDuration.apply {
                                this.value = 0
                            },
                            isSongPlaying = state.isSongPlaying.apply {
                                this.value = false
                            },
                        )
                    }
                }
            }
        }
    }
}
private fun updatePlaybackState(currentPosition: Int) {
        _musicPlayerState.update {
            it.copy(
                playingSongCurrentPosition = it.playingSongCurrentPosition.apply {
                    this.value = currentPosition
                }
            )
        }
    }

    private fun setSongDuration(duration: Int) {
        _musicPlayerState.update {
            it.copy(
                playingSongDuration = it.playingSongDuration.apply {
                    this.value = duration
                }
            )
        }
    }
 Box(
            modifier =
            Modifier
                .padding(vertical = 80.dp, horizontal = 20.dp)
                .fillMaxWidth()
                .height(20.dp)
        ) {
            var fraction by remember { mutableFloatStateOf(1f) }
            WavySlider(
                valueRange = 1000f..state.playingSongDuration.value.toFloat(),
                value = 1000f,
                onValueChange = { },
                waveLength = 25.dp,     // Set this to 0.dp to get a regular Slider
                waveHeight = 10.dp,     // Set this to 0.dp to get a regular Slider
                waveVelocity = 15.dp to WaveDirection.HEAD, // Speed per second and its direction
                waveThickness = 4.dp,   // Defaults to the specified trackThickness
                trackThickness = 4.dp,  // Defaults to 4.dp, same as regular Slider
                incremental = false,    // Whether to gradually increase waveHeight
                // animationSpecs = ... // Customize various animations of the wave
            )
        }


Solution

  • i want it should update every single second

    In order to do this, you must implement some kind of asynchronous loop, using a Coroutine, or Handler, to update the state on an interval using delay() if using a Coroutine or postDelayed() if using a Handler. You can get the exact position of the MediaPlayer using getCurrentPosition()

    Here is some example code in Kotlin using a Coroutine

    private suspend fun seekbarUpdateObserver() {
        withContext(Dispatchers.IO) {
            while (true) {
                if (mediaPlayer != null && mediaPlayer!!.isPlaying) {
                    val pos = mediaPlayer!!.currentPosition
                    val progress = (pos.toFloat() / mediaPlayer!!.duration) * 100f
                    // TODO update SeekBar with progress
                }
    
                delay(17L)
            }
        }
    }
    

    I would recommend an interval of every 17 milliseconds to achieve 60 fps as 1000 ms / 60 ms = ~17 ms

    This function can then be called after start using scope.launch()

     private var scope = MainScope()
     // ...
     mediaPlayer!!.start()
     scope.launch { seekbarUpdateObserver() }
    

    and cancelled using the scope.cancel() function

    mediaPlayer!!.stop()
    mediaPlayer!!.release()
    mediaPlayer = null
    scope.cancel()