General improvements (#50)

* Implemented long-press gestures(on the left and right region) for rapid scrolling(to top or bottom) in Musician Mode for both EPUB and PDF readers.

* Added support for book-specific reader settings and redesigned the format adjustment UI.

* perf: implement persistent caching and parallel parsing for single-file imports

- Replaced random UUID generation with bookId-based pathing in SingleFileImporter to enable cache reuse.
- Added a JSON metadata fast-path to return EpubBook objects instantly on subsequent opens for MD, TXT, and HTML.
- Parallelized Markdown chapter parsing using Coroutines (async/awaitAll) to leverage multicore CPUs.
This commit is contained in:
Aryan 2026-03-09 23:09:56 +05:30 committed by GitHub
parent ef1bfdc57a
commit acf282d4c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 684 additions and 299 deletions

View file

@ -18,7 +18,7 @@
* mail: epistemereader@gmail.com
*/
// PdfViewerScreen.kt
@file:Suppress("COMPOSE_APPLIER_CALL_MISMATCH")
@file:Suppress("COMPOSE_APPLIER_CALL_MISMATCH", "Unused", "UnusedVariable")
package com.aryan.reader.pdf
@ -27,6 +27,9 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
import android.graphics.Bitmap
import kotlin.math.max
import android.graphics.RectF
@ -4286,6 +4289,10 @@ fun PdfViewerScreen(
var leftPulseTrigger by remember { mutableLongStateOf(0L) }
var rightPulseTrigger by remember { mutableLongStateOf(0L) }
// --- ADD THESE STATES ---
var leftHoldProgress by remember { mutableFloatStateOf(0f) }
var rightHoldProgress by remember { mutableFloatStateOf(0f) }
val leftPulseAlpha by animateFloatAsState(
targetValue = if (System.currentTimeMillis() - leftPulseTrigger < 150) 0.3f else 0f,
animationSpec = tween(150), label = "leftPulse"
@ -4302,6 +4309,7 @@ fun PdfViewerScreen(
val scrollAmount = boxMaxHeightFloat * 0.75f
// Left Region
Box(
modifier = regionWidth
.then(regionHeight)
@ -4310,22 +4318,65 @@ fun PdfViewerScreen(
.padding(start = 8.dp)
.background(Color.White.copy(alpha = leftPulseAlpha), RoundedCornerShape(12.dp))
.border(2.dp, Color.Gray.copy(alpha = 0.3f), RoundedCornerShape(12.dp))
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = {
Timber.tag("MusicianMode").d("Left region tapped")
leftPulseTrigger = System.currentTimeMillis()
.pointerInput(Unit) {
awaitEachGesture {
val down = awaitFirstDown()
var isLongPress = false
val job = coroutineScope.launch {
val startTime = System.currentTimeMillis()
while (isActive) {
val elapsed = System.currentTimeMillis() - startTime
if (elapsed >= 1000) {
leftHoldProgress = 0f
isLongPress = true
leftPulseTrigger = System.currentTimeMillis()
triggerAutoScrollTempPause(1000L)
triggerAutoScrollTempPause(600L)
coroutineScope.launch {
verticalReaderState.scrollToPage(0)
}
break
}
leftHoldProgress = elapsed / 1000f
delay(16)
}
}
coroutineScope.launch {
verticalReaderState.scrollBy(-scrollAmount)
val up = waitForUpOrCancellation()
job.cancel()
leftHoldProgress = 0f
if (!isLongPress && up != null) {
up.consume()
Timber.tag("MusicianMode").d("Left region tapped")
leftPulseTrigger = System.currentTimeMillis()
triggerAutoScrollTempPause(600L)
coroutineScope.launch {
verticalReaderState.scrollBy(-scrollAmount)
}
}
}
},
contentAlignment = Alignment.Center
) {
if (leftHoldProgress > 0f) {
CircularProgressIndicator(
progress = { leftHoldProgress },
modifier = Modifier.size(48.dp).alpha(0.6f),
color = MaterialTheme.colorScheme.primary,
trackColor = Color.Transparent,
strokeWidth = 4.dp
)
)
Icon(
imageVector = Icons.Default.ArrowUpward,
contentDescription = null,
modifier = Modifier.size(24.dp).alpha(0.6f),
tint = MaterialTheme.colorScheme.primary
)
}
}
// Right Region
Box(
modifier = regionWidth
.then(regionHeight)
@ -4334,22 +4385,63 @@ fun PdfViewerScreen(
.padding(end = 8.dp)
.background(Color.White.copy(alpha = rightPulseAlpha), RoundedCornerShape(12.dp))
.border(2.dp, Color.Gray.copy(alpha = 0.3f), RoundedCornerShape(12.dp))
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = {
Timber.tag("MusicianMode").d("Right region tapped")
rightPulseTrigger = System.currentTimeMillis() // Trigger flash
.pointerInput(totalPages) {
awaitEachGesture {
val down = awaitFirstDown()
var isLongPress = false
val job = coroutineScope.launch {
val startTime = System.currentTimeMillis()
while (isActive) {
val elapsed = System.currentTimeMillis() - startTime
if (elapsed >= 1000) {
rightHoldProgress = 0f
isLongPress = true
rightPulseTrigger = System.currentTimeMillis()
triggerAutoScrollTempPause(1000L)
// Pause loop to allow smooth scroll
triggerAutoScrollTempPause(600L)
coroutineScope.launch {
verticalReaderState.scrollToPage(totalPages - 1)
}
break
}
rightHoldProgress = elapsed / 1000f
delay(16)
}
}
coroutineScope.launch {
verticalReaderState.scrollBy(scrollAmount)
val up = waitForUpOrCancellation()
job.cancel()
rightHoldProgress = 0f
if (!isLongPress && up != null) {
up.consume()
Timber.tag("MusicianMode").d("Right region tapped")
rightPulseTrigger = System.currentTimeMillis()
triggerAutoScrollTempPause(600L)
coroutineScope.launch {
verticalReaderState.scrollBy(scrollAmount)
}
}
}
},
contentAlignment = Alignment.Center
) {
if (rightHoldProgress > 0f) {
CircularProgressIndicator(
progress = { rightHoldProgress },
modifier = Modifier.size(48.dp).alpha(0.6f),
color = MaterialTheme.colorScheme.primary,
trackColor = Color.Transparent,
strokeWidth = 4.dp
)
)
Icon(
imageVector = Icons.Default.ArrowDownward,
contentDescription = null,
modifier = Modifier.size(24.dp).alpha(0.6f),
tint = MaterialTheme.colorScheme.primary
)
}
}
}
}