Auto scroll update (#7)

* updated auto-scroll logic for EPUB and PDF

* Refactored auto-scroll to use temporary debounced pauses instead of hard stops on manual interaction.
* Updated tap handling to allow toggling bars without interrupting scroll.
* Added a 2-second orientation pause on EPUB chapter transitions.
* Enhanced AutoScrollControls UI with circular progress overlay to indicate the "temporarily paused" state.

* Refactored auto-scroll controls

This commit refactors the auto-scroll controls for both EPUB and PDF readers, introducing a more advanced and user-friendly interface.

Key changes:
- Redesigned the auto-scroll controls UI into a collapsible and lockable card.
- Added a "lock" feature to hide controls during scrolling.
- Introduced an alternative slider-based input for speed adjustment, which can be toggled.
- Persisted the lock and input mode states in SharedPreferences.
- Improved the EPUB auto-scroll mechanism using `translate3d` for smoother sub-pixel scrolling.
This commit is contained in:
Aryan 2026-02-26 17:16:01 +05:30 committed by GitHub
parent 1f111034f1
commit de376e2ace
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 410 additions and 238 deletions

View file

@ -17,6 +17,7 @@
*
* mail: epistemereader@gmail.com
*/
// EpubReaderScreen.kt
@file:OptIn(ExperimentalSerializationApi::class) @file:Suppress("VariableNeverRead")
package com.aryan.reader.epubreader
@ -171,6 +172,30 @@ import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
import androidx.compose.ui.BiasAlignment
import androidx.core.content.edit
private const val AUTO_SCROLL_LOCKED_KEY = "auto_scroll_locked"
private const val AUTO_SCROLL_USE_SLIDER_KEY = "auto_scroll_use_slider"
private fun saveAutoScrollLocked(context: Context, isLocked: Boolean) {
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
prefs.edit { putBoolean(AUTO_SCROLL_LOCKED_KEY, isLocked)}
}
private fun loadAutoScrollLocked(context: Context): Boolean {
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
return prefs.getBoolean(AUTO_SCROLL_LOCKED_KEY, false)
}
private fun saveAutoScrollUseSlider(context: Context, useSlider: Boolean) {
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
prefs.edit { putBoolean(AUTO_SCROLL_USE_SLIDER_KEY, useSlider) }
}
private fun loadAutoScrollUseSlider(context: Context): Boolean {
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
return prefs.getBoolean(AUTO_SCROLL_USE_SLIDER_KEY, false)
}
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Composable
@ -607,9 +632,14 @@ fun EpubReaderHost(
var isAutoScrollModeActive by remember { mutableStateOf(false) }
var isAutoScrollPlaying by remember { mutableStateOf(false) }
var isAutoScrollTempPaused by remember { mutableStateOf(false) }
val autoScrollResumeJob = remember { mutableStateOf<Job?>(null) }
var autoScrollSpeed by remember { mutableFloatStateOf(loadAutoScrollSpeed(context)) }
var isAutoScrollCollapsed by remember { mutableStateOf(false) }
var isAutoScrollLocked by remember { mutableStateOf(loadAutoScrollLocked(context)) }
var autoScrollUseSlider by remember { mutableStateOf(loadAutoScrollUseSlider(context)) }
DisposableEffect(Unit) {
onDispose {
Timber.d("Disposing sample MediaPlayer.")
@ -618,10 +648,28 @@ fun EpubReaderHost(
}
fun updateAutoScrollState(playing: Boolean, speed: Float) {
updateAutoScrollJs(webViewRefForTts, playing, speed)
val effectivePlaying = playing && !isAutoScrollTempPaused
updateAutoScrollJs(webViewRefForTts, effectivePlaying, speed)
}
LaunchedEffect(isAutoScrollModeActive, isAutoScrollPlaying, autoScrollSpeed) {
fun triggerAutoScrollTempPause(durationMs: Long) {
if (!isAutoScrollModeActive || !isAutoScrollPlaying) return
autoScrollResumeJob.value?.cancel()
isAutoScrollTempPaused = true
updateAutoScrollState(isAutoScrollPlaying, autoScrollSpeed)
autoScrollResumeJob.value = scope.launch {
delay(durationMs)
if (isActive && isAutoScrollModeActive && isAutoScrollPlaying) {
isAutoScrollTempPaused = false
@Suppress("KotlinConstantConditions") updateAutoScrollState(isAutoScrollPlaying, autoScrollSpeed)
}
}
}
LaunchedEffect(isAutoScrollModeActive, isAutoScrollPlaying, autoScrollSpeed, isAutoScrollTempPaused) {
if (isAutoScrollModeActive) {
updateAutoScrollState(isAutoScrollPlaying, autoScrollSpeed)
} else {
@ -629,12 +677,6 @@ fun EpubReaderHost(
}
}
fun pauseAutoScroll() {
if (isAutoScrollModeActive && isAutoScrollPlaying) {
isAutoScrollPlaying = false
}
}
fun startTts() {
if (isAutoScrollModeActive) {
isAutoScrollModeActive = false
@ -1615,11 +1657,8 @@ fun EpubReaderHost(
}
if (isAutoScrollModeActive && isAutoScrollPlaying) {
Timber.d("Continuing Auto-Scroll for new chapter.")
scope.launch {
delay(300)
updateAutoScrollState(true, autoScrollSpeed)
}
Timber.d("Continuing Auto-Scroll for new chapter with delay.")
triggerAutoScrollTempPause(1000L)
}
},
onTap = {
@ -1637,7 +1676,6 @@ fun EpubReaderHost(
Timber.d("Chapter tapped, showing main bars.")
}
}
pauseAutoScroll()
},
onPotentialScroll = {
if (showBars) {
@ -1645,7 +1683,9 @@ fun EpubReaderHost(
showFormatAdjustmentBars = false
Timber.d("Scroll/Drag detected, hiding bars.")
}
pauseAutoScroll()
if (isAutoScrollModeActive && isAutoScrollPlaying) {
triggerAutoScrollTempPause(1000L)
}
},
onAutoScrollChapterEnd = {
Timber.d("Screen: onAutoScrollChapterEnd triggered. Current Index: $currentChapterIndex")
@ -2620,6 +2660,7 @@ fun EpubReaderHost(
isAutoScrollModeActive = true
isAutoScrollPlaying = true
showBars = false
showBars = true
},
searchFocusRequester = searchFocusRequester,
modifier = Modifier.align(Alignment.TopCenter)
@ -2635,8 +2676,10 @@ fun EpubReaderHost(
label = "AutoScrollAlignAnimation"
)
val isAutoScrollControlsVisible = isAutoScrollModeActive && (!isAutoScrollLocked || showBars)
AnimatedVisibility(
visible = isAutoScrollModeActive,
visible = isAutoScrollControlsVisible,
enter = slideInVertically { it } + fadeIn(),
exit = slideOutVertically { it } + fadeOut(),
modifier = Modifier
@ -2646,8 +2689,19 @@ fun EpubReaderHost(
) {
AutoScrollControls(
isPlaying = isAutoScrollPlaying,
onPlayPauseToggle = { isAutoScrollPlaying = !isAutoScrollPlaying },
isTempPaused = isAutoScrollTempPaused,
onPlayPauseToggle = {
if (isAutoScrollPlaying) {
isAutoScrollPlaying = false
isAutoScrollTempPaused = false
autoScrollResumeJob.value?.cancel()
} else {
isAutoScrollPlaying = true
isAutoScrollTempPaused = false
}
},
speed = autoScrollSpeed,
maxSpeed = 10f,
onSpeedChange = {
autoScrollSpeed = it
saveAutoScrollSpeed(context, it)
@ -2658,7 +2712,17 @@ fun EpubReaderHost(
showBars = true
},
isCollapsed = isAutoScrollCollapsed,
onCollapseChange = { isAutoScrollCollapsed = it }
onCollapseChange = { isAutoScrollCollapsed = it },
isLocked = isAutoScrollLocked,
onLockToggle = {
isAutoScrollLocked = !isAutoScrollLocked
saveAutoScrollLocked(context, isAutoScrollLocked)
},
useSlider = autoScrollUseSlider,
onInputModeToggle = {
autoScrollUseSlider = !autoScrollUseSlider
saveAutoScrollUseSlider(context, autoScrollUseSlider)
}
)
}