From 0e3882eff8021cb7d6615323b358c64dc4240904 Mon Sep 17 00:00:00 2001 From: KhooLy <73142442+KhooLy@users.noreply.github.com> Date: Fri, 3 Jul 2026 12:45:24 +0300 Subject: [PATCH] Add seek acceleration to TV seekbar on held D-pad Consecutive same-direction presses ramp up the jump amount: first 2 presses use the profile seek interval, presses 3-7 triple it, and presses 8+ sextuple it. Direction change or focus loss resets the counter. This lets quick taps do fine scrubbing while holding the key covers large spans fast. --- .../fluxa/app/ui/catalog/TvPlayerControls.kt | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/fluxa/app/ui/catalog/TvPlayerControls.kt b/app/src/main/java/com/fluxa/app/ui/catalog/TvPlayerControls.kt index 331d5c1..9aab808 100644 --- a/app/src/main/java/com/fluxa/app/ui/catalog/TvPlayerControls.kt +++ b/app/src/main/java/com/fluxa/app/ui/catalog/TvPlayerControls.kt @@ -51,6 +51,8 @@ fun TVSeekbar( ) { var isFocused by remember { mutableStateOf(false) } var internalPos by remember { mutableFloatStateOf(position.toFloat()) } + var consecutivePresses by remember { mutableIntStateOf(0) } + var lastPressDirection by remember { mutableIntStateOf(0) } val seekbarAccent = FluxaColors.accent val seekPreview = rememberSeekThumbnail(LocalSeekSurfaceView.current, internalPos.toLong(), isFocused, position, isPlaying, onScrubSeek) @@ -73,29 +75,37 @@ fun TVSeekbar( .fillMaxWidth() .height(48.dp) .focusRequester(focusRequester) - .onFocusChanged { - isFocused = it.isFocused - onScrubbing(it.isFocused, internalPos.toLong()) + .onFocusChanged { focusState -> + isFocused = focusState.isFocused + if (!focusState.isFocused) { consecutivePresses = 0; lastPressDirection = 0 } + onScrubbing(focusState.isFocused, internalPos.toLong()) } .focusProperties { up = playPauseFocusRequester } .focusable() - .onKeyEvent { + .onKeyEvent { if (it.type == KeyEventType.KeyDown) { when (it.key) { - Key.DirectionLeft -> { - internalPos = (internalPos - seekBackwardMs).coerceAtLeast(0f) + Key.DirectionLeft -> { + if (lastPressDirection != -1) { consecutivePresses = 0; lastPressDirection = -1 } + consecutivePresses++ + val multiplier = when { consecutivePresses >= 8 -> 6L; consecutivePresses >= 3 -> 3L; else -> 1L } + internalPos = (internalPos - seekBackwardMs * multiplier).coerceAtLeast(0f) onScrubbing(true, internalPos.toLong()) - true + true } - Key.DirectionRight -> { - internalPos = (internalPos + seekForwardMs).coerceAtMost(duration.toFloat()) + Key.DirectionRight -> { + if (lastPressDirection != 1) { consecutivePresses = 0; lastPressDirection = 1 } + consecutivePresses++ + val multiplier = when { consecutivePresses >= 8 -> 6L; consecutivePresses >= 3 -> 3L; else -> 1L } + internalPos = (internalPos + seekForwardMs * multiplier).coerceAtMost(duration.toFloat()) onScrubbing(true, internalPos.toLong()) - true + true } - Key.Enter, Key.DirectionCenter -> { + Key.Enter, Key.DirectionCenter -> { + consecutivePresses = 0; lastPressDirection = 0 onSeek(internalPos.toLong()) onScrubbing(false, internalPos.toLong()) - true + true } else -> false }