player: debounce the buffering indicator

ExoPlayer flaps READY↔BUFFERING at every buffer edge and seek; each
flap tore the red spinner overlay out of composition and rebuilt it,
visibly restarting the animation from zero and flickering the covering
box — the "struggles and restarts" report. The displayed state now
follows the raw player state debounced, last value wins after 200 ms,
so quick flaps cancel out and the overlay never churns mid-playback.
The long initial buffering still shows it immediately.

Closes #17
This commit is contained in:
Jeena 2026-10-14 00:20:50 +00:00
parent 33abf61cd1
commit b2601bb0c0

View file

@ -286,6 +286,17 @@ private fun PlayerSurface(
// a re-buffer after seeking) — shows an indicator until media really plays // a re-buffer after seeking) — shows an indicator until media really plays
// (issue #8: the Loading screen ended too early and the rest was black). // (issue #8: the Loading screen ended too early and the rest was black).
var buffering by remember { mutableStateOf(true) } var buffering by remember { mutableStateOf(true) }
// The DISPLAYED overlay follows the raw player state debounced (issue #17):
// ExoPlayer flaps READY↔BUFFERING at every buffer edge and seek — tearing
// the overlay out of composition on each flap restarted the red spinner
// from zero ("it struggles and restarts"). Last pending value wins after
// 200 ms: quick flaps cancel the pending change and the overlay stays
// composed, so the animation never restarts mid-playback.
var bufferingPending by remember { mutableStateOf(true) }
LaunchedEffect(bufferingPending) {
kotlinx.coroutines.delay(200)
buffering = bufferingPending
}
// Key press that was consumed at ACTION_DOWN — its trailing ACTION_UP must not // Key press that was consumed at ACTION_DOWN — its trailing ACTION_UP must not
// re-reveal the native controller right after a play/pause toggle. // re-reveal the native controller right after a play/pause toggle.
var consumedDownKey by remember { mutableStateOf(0) } var consumedDownKey by remember { mutableStateOf(0) }
@ -393,7 +404,7 @@ private fun PlayerSurface(
} }
override fun onPlaybackStateChanged(playbackState: Int) { override fun onPlaybackStateChanged(playbackState: Int) {
buffering = playbackState != Player.STATE_READY bufferingPending = playbackState != Player.STATE_READY
} }
}) })
onDispose { onDispose {