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:
parent
1f111034f1
commit
de376e2ace
6 changed files with 410 additions and 238 deletions
|
|
@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue