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
)
}
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()