I use LaunchedEffect and while to get the currentTime on time, but there is a problem when I press the pause and play buttons, the video repeats
PlayerScreen.kt
@OptIn(UnstableApi::class)
@Composable
fun PlayerScreen(
navController: NavHostController,
modifier: Modifier = Modifier
) {
val videoUrl = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
val context = LocalContext.current
val player = remember { ExoPlayer.Builder(context).build() }
val playerView = remember { PlayerView(context) }
val mediaItem = remember { MediaItem.fromUri(videoUrl) }
var isPlaying by rememberSaveable { mutableStateOf(true) }
player.setMediaItem(mediaItem)
playerView.player = player
LaunchedEffect(player){
player.prepare()
player.playWhenReady = isPlaying
}
var currentTime by remember { mutableLongStateOf(1L) }
var maxDuration by remember { mutableLongStateOf(50L) }
if (isPlaying) {
LaunchedEffect(isPlaying) { // Menggunakan isPlaying sebagai dependencies
while(true) {
currentTime = player.currentPosition
if(player.isPlaying){
maxDuration = player.duration
}
delay(1.seconds / 30)
}
}
}
DisposableEffect(player) {
onDispose {
player.release()
}
}
Box(modifier = modifier) {
AndroidView(factory = {
playerView.apply {
// resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
useController = false
layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
}
})
ControllerPlayer(
isPlaying = { isPlaying },
onReplayClick = { player.seekBack() },
onPauseToggle = {
if (player.isPlaying) {
player.pause()
} else {
player.play()
}
isPlaying = isPlaying.not()
},
onForwardClick = { player.seekForward() },
totalDuration = { maxDuration },
currentTime = { currentTime },
bufferPercentage = { player.bufferedPercentage },
onSeekChanged = { newPosition ->
player.seekTo(newPosition.toLong())
},
isVisibleController = {true}
)
}
}
I don't think there's a problem with the code, but I'll still give you the code ControllerScreen.kt
@SuppressLint("RememberReturnType")
@OptIn(UnstableApi::class)
@Composable
fun BoxScope.ControllerPlayer(
modifier: Modifier = Modifier,
isPlaying: () -> Boolean,
onReplayClick: () -> Unit,
onPauseToggle: () -> Unit,
onForwardClick: () -> Unit,
totalDuration: () -> Long,
currentTime: () -> Long,
bufferPercentage: () -> Int,
onSeekChanged: (timeMs: Float) -> Unit,
isVisibleController: () -> Boolean,
) {
val isVideoPlaying = remember(isPlaying()) { isPlaying() }
val visible = remember(isVisibleController()) { isVisibleController() }
val duration = remember(totalDuration()) { totalDuration() }
val videoTime = remember(currentTime()) { currentTime() }
val buffer = remember(bufferPercentage()) { bufferPercentage() }
AnimatedVisibility(
visible = if(AppConfig.isProduction()) visible else true,
enter = fadeIn(tween(200)),
exit = fadeOut(tween(200))
) {
Box(modifier = modifier.fillMaxSize()){
Row(
modifier = modifier
.align(Alignment.Center),
horizontalArrangement = Arrangement.SpaceEvenly
) {
IconButton(modifier = Modifier.size(40.dp), onClick = onReplayClick) {
Image(
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
painter = painterResource(id = androidx.media3.ui.R.drawable.exo_ic_chevron_left),
contentDescription = "Replay 5 seconds"
)
}
IconButton(modifier = Modifier.size(40.dp), onClick = onPauseToggle) {
Image(
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
painter =
if (isVideoPlaying) {
painterResource(id = androidx.media3.ui.R.drawable.exo_icon_pause)
} else {
painterResource(id = androidx.media3.ui.R.drawable.exo_icon_play)
},
contentDescription = "Play/Pause"
)
}
IconButton(modifier = Modifier.size(40.dp), onClick = onForwardClick) {
Image(
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
painter = painterResource(id = androidx.media3.ui.R.drawable.exo_ic_chevron_right),
contentDescription = "Forward 10 seconds"
)
}
}
Column(
modifier = modifier
.align(Alignment.BottomCenter)
.padding(bottom = 32.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Bottom
) {
Text(
modifier = Modifier.padding(horizontal = 16.dp),
text = "${videoTime.formatMinSec()} / ${duration.formatMinSec()}",
color = DarkTheme.onSecondary
)
IconButton(
modifier = Modifier.padding(horizontal = 16.dp),
onClick = {}
) {
Image(
contentScale = ContentScale.Crop,
painter = painterResource(id = androidx.media3.ui.R.drawable.exo_icon_fullscreen_enter),
contentDescription = "Enter/Exit fullscreen"
)
}
}
Box(modifier = Modifier.fillMaxWidth()) {
// buffer bar
Slider(
value = buffer.toFloat(),
enabled = false,
onValueChange = { /*do nothing*/ },
valueRange = 0f..100f,
colors =
SliderDefaults.colors(
disabledThumbColor = Color.Transparent,
disabledActiveTrackColor = Color.Gray
)
)
// seek bar
Slider(
modifier = Modifier.fillMaxWidth(),
value = videoTime.toFloat(),
onValueChange = onSeekChanged,
valueRange = 0f..duration.toFloat(),
colors =
SliderDefaults.colors(
thumbColor = DarkTheme.tertiary,
activeTickColor = DarkTheme.onSecondary
)
)
}
}
}
}
}
fun Long.formatMinSec(): String {
return if (this == 0L) {
"..."
} else {
String.format(
"%02d:%02d",
TimeUnit.MILLISECONDS.toMinutes(this),
TimeUnit.MILLISECONDS.toSeconds(this) -
TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(this)
)
)
}
}
I want if I press the pause button and play the video doesn't repeat itself
I ran your code into my system and I found one solution.
When you are click on pause button then you are switching the value of isPlaying with remember but it affects other place as well.so i just create other variable and when click on pause then i am just changing the value of that other varible then it worked perfectly.
just create one other variable
put this isVideoContinue variable in the ControllerPlayer
well I am not sure but you are using isPlaying on playWhenReady so it's the possible reason that whenever isPlaying variable changing while clicking on pause button then it will restart the video.