Pdf auto scroll (#5)
* docs: Revamp README and add APK naming convention This commit introduces a complete overhaul of the `README.md` to improve project presentation. Key changes include: * Adds a new header with the app icon and a Google Play badge. * Updates the feature graphic and includes a note distinguishing the OSS version from the Play Store version. * Refines the "Open Source Libraries" section to be more structured. * Moves the main preview image into the `docs` directory. Additionally, the `app/build.gradle.kts` file is updated to configure a standardized naming convention for output APK files, including flavor, version, and build type. * refactor copyright headers and fix README icon alignment This commit introduces two main changes across the codebase: The `spotless/copyright.kt` template have been updated for consistency: * The author name is simplified from "Episteme Authors" to "Episteme". * The contact email prefix is shortened from "Electronic mail:" to "mail:". Second, the `README.md` file is adjusted to correct the vertical alignment of the app icon with the project title. * Added auto-scroll feature for PDF vertical reader
This commit is contained in:
parent
1f35cd8138
commit
1b048c9073
3 changed files with 127 additions and 7 deletions
|
|
@ -17,6 +17,7 @@
|
|||
*
|
||||
* mail: epistemereader@gmail.com
|
||||
*/
|
||||
// PdfVerticalReader.kt
|
||||
@file:Suppress("COMPOSE_APPLIER_CALL_MISMATCH", "VariableNeverRead")
|
||||
|
||||
package com.aryan.reader.pdf
|
||||
|
|
@ -210,7 +211,10 @@ internal fun PdfVerticalReader(
|
|||
onTextBoxSelect: (String) -> Unit = {},
|
||||
bottomContentPaddingPx: Float = 0f,
|
||||
topContentPaddingPx: Float = 0f,
|
||||
onTextBoxMoved: (String, Int, Rect) -> Unit = { _, _, _ -> }
|
||||
onTextBoxMoved: (String, Int, Rect) -> Unit = { _, _, _ -> },
|
||||
isAutoScrollPlaying: Boolean = false,
|
||||
autoScrollSpeed: Float = 1.0f,
|
||||
onInteractionListener: () -> Unit = {}
|
||||
) {
|
||||
SideEffect { Timber.tag("PdfDrawPerf").v("LIST: PdfVerticalReader Recomposing.") }
|
||||
var globalEraserPosition by remember { mutableStateOf<Offset?>(null) }
|
||||
|
|
@ -480,9 +484,46 @@ internal fun PdfVerticalReader(
|
|||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(isAutoScrollPlaying, autoScrollSpeed, totalDocHeight, screenHeight) {
|
||||
if (isAutoScrollPlaying) {
|
||||
val baseSpeedPxPerSec = 30f
|
||||
var lastFrameTime = withFrameNanos { it }
|
||||
|
||||
while (isActive) {
|
||||
val frameTime = withFrameNanos { it }
|
||||
val deltaSeconds = (frameTime - lastFrameTime) / 1_000_000_000f
|
||||
lastFrameTime = frameTime
|
||||
|
||||
if (deltaSeconds > 0.1f) continue
|
||||
|
||||
val pixelMove = (baseSpeedPxPerSec * autoScrollSpeed) * deltaSeconds
|
||||
|
||||
val currentPanY = panYAnimatable.value
|
||||
val currentZoom = zoomAnimatable.value
|
||||
val zoomedDocHeight = totalDocHeight * currentZoom
|
||||
|
||||
val minPanY = (screenHeight - footerHeightPx - zoomedDocHeight).coerceAtMost(headerHeightPx)
|
||||
|
||||
val newPanY = (currentPanY - pixelMove).coerceIn(minPanY, headerHeightPx)
|
||||
|
||||
panYAnimatable.snapTo(newPanY)
|
||||
|
||||
@Suppress("ControlFlowWithEmptyBody") if (newPanY <= minPanY + 0.1f) {
|
||||
// Reached end
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var highResScale by remember { mutableFloatStateOf(1f) }
|
||||
var isInteracting by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(isInteracting) {
|
||||
if (isInteracting && isAutoScrollPlaying) {
|
||||
onInteractionListener()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(isInteracting) {
|
||||
Timber.tag("PdfTouchDebug").i("VerticalReader: isInteracting changed to $isInteracting")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
*
|
||||
* mail: epistemereader@gmail.com
|
||||
*/
|
||||
// PdfViewerScreen.kt
|
||||
@file:Suppress("COMPOSE_APPLIER_CALL_MISMATCH")
|
||||
|
||||
package com.aryan.reader.pdf
|
||||
|
|
@ -159,6 +160,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.BiasAlignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -227,6 +229,7 @@ import com.aryan.reader.SearchTopBar
|
|||
import com.aryan.reader.SummarizationPopup
|
||||
import com.aryan.reader.SummarizationResult
|
||||
import com.aryan.reader.SyncUpdateInfo
|
||||
import com.aryan.reader.epubreader.AutoScrollControls
|
||||
import com.aryan.reader.fetchAiDefinition
|
||||
import com.aryan.reader.paginatedreader.TtsChunk
|
||||
import com.aryan.reader.pdf.data.AnnotationSettingsRepository
|
||||
|
|
@ -280,6 +283,17 @@ private const val OCR_LANGUAGE_SELECTED_KEY = "ocr_language_selected_key"
|
|||
private const val DOCK_LOCATION_KEY = "dock_location"
|
||||
private const val DOCK_OFFSET_X_KEY = "dock_offset_x"
|
||||
private const val DOCK_OFFSET_Y_KEY = "dock_offset_y"
|
||||
private const val PDF_AUTO_SCROLL_SPEED_KEY = "pdf_auto_scroll_speed"
|
||||
|
||||
private fun savePdfAutoScrollSpeed(context: Context, speed: Float) {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit { putFloat(PDF_AUTO_SCROLL_SPEED_KEY, speed) }
|
||||
}
|
||||
|
||||
private fun loadPdfAutoScrollSpeed(context: Context): Float {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
return prefs.getFloat(PDF_AUTO_SCROLL_SPEED_KEY, 3.0f)
|
||||
}
|
||||
|
||||
private fun generateShortId(): String {
|
||||
return Random.nextInt(1000, 9999).toString()
|
||||
|
|
@ -586,6 +600,23 @@ fun PdfViewerScreen(
|
|||
}
|
||||
}
|
||||
|
||||
var isDockDragging by remember { mutableStateOf(false) }
|
||||
|
||||
var isAutoScrollModeActive by remember { mutableStateOf(false) }
|
||||
var isAutoScrollPlaying by remember { mutableStateOf(false) }
|
||||
var autoScrollSpeed by remember { mutableFloatStateOf(loadPdfAutoScrollSpeed(context)) }
|
||||
var isAutoScrollCollapsed by remember { mutableStateOf(false) }
|
||||
|
||||
val onAutoScrollInteraction = remember {
|
||||
{
|
||||
if (isAutoScrollPlaying) {
|
||||
isAutoScrollPlaying = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var paginationDraggingBoxId by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
val customFonts by viewModel.customFonts.collectAsState()
|
||||
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
|
@ -609,9 +640,6 @@ fun PdfViewerScreen(
|
|||
var dockLocation by remember { mutableStateOf(initialDockLocation) }
|
||||
var dockOffset by remember { mutableStateOf(initialDockOffset) }
|
||||
var snapPreviewLocation by remember { mutableStateOf<DockLocation?>(null) }
|
||||
var isDockDragging by remember { mutableStateOf(false) }
|
||||
|
||||
var paginationDraggingBoxId by remember { mutableStateOf<String?>(null) }
|
||||
var paginationDraggingOffset by remember { mutableStateOf(Offset.Zero) }
|
||||
var paginationDraggingSize by remember { mutableStateOf(Size.Zero) }
|
||||
var paginationDragPageHeight by remember { mutableFloatStateOf(0f) }
|
||||
|
|
@ -1074,6 +1102,7 @@ fun PdfViewerScreen(
|
|||
|
||||
val onSingleTapStable = remember {
|
||||
{
|
||||
onAutoScrollInteraction()
|
||||
if (selectedTextBoxId != null) {
|
||||
val box = textBoxes.find { it.id == selectedTextBoxId }
|
||||
if (box != null && box.text.trim().isEmpty()) {
|
||||
|
|
@ -3610,7 +3639,10 @@ fun PdfViewerScreen(
|
|||
val oldBox = textBoxes[idx]
|
||||
textBoxes[idx] = oldBox.copy(pageIndex = newPageIndex, relativeBounds = newBounds)
|
||||
}
|
||||
}
|
||||
},
|
||||
isAutoScrollPlaying = isAutoScrollPlaying,
|
||||
autoScrollSpeed = autoScrollSpeed,
|
||||
onInteractionListener = onAutoScrollInteraction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -4077,6 +4109,17 @@ fun PdfViewerScreen(
|
|||
}
|
||||
})
|
||||
HorizontalDivider()
|
||||
DropdownMenuItem(
|
||||
text = { Text("Auto Scroll") },
|
||||
enabled = !isTtsSessionActive && displayMode == DisplayMode.VERTICAL_SCROLL,
|
||||
onClick = {
|
||||
showMoreMenu = false
|
||||
isAutoScrollModeActive = true
|
||||
isAutoScrollPlaying = true
|
||||
showBars = false
|
||||
}
|
||||
)
|
||||
HorizontalDivider()
|
||||
DropdownMenuItem(text = {
|
||||
Text(
|
||||
if (isBookmarked) "Remove bookmark"
|
||||
|
|
@ -5506,6 +5549,42 @@ fun PdfViewerScreen(
|
|||
}
|
||||
}
|
||||
}
|
||||
val autoScrollPadding by animateDpAsState(
|
||||
targetValue = if (showBars) (56.dp + 16.dp) else 16.dp,
|
||||
label = "AutoScrollPadding"
|
||||
)
|
||||
|
||||
val alignmentBias by animateFloatAsState(
|
||||
targetValue = if (isAutoScrollCollapsed) 1f else 0f,
|
||||
label = "AutoScrollAlignAnimation"
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = isAutoScrollModeActive,
|
||||
enter = slideInVertically { it } + fadeIn(),
|
||||
exit = slideOutVertically { it } + fadeOut(),
|
||||
modifier = Modifier
|
||||
.align(BiasAlignment(alignmentBias, 1f))
|
||||
.padding(bottom = autoScrollPadding)
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
AutoScrollControls(
|
||||
isPlaying = isAutoScrollPlaying,
|
||||
onPlayPauseToggle = { isAutoScrollPlaying = !isAutoScrollPlaying },
|
||||
speed = autoScrollSpeed,
|
||||
onSpeedChange = {
|
||||
autoScrollSpeed = it
|
||||
savePdfAutoScrollSpeed(context, it)
|
||||
},
|
||||
onClose = {
|
||||
isAutoScrollModeActive = false
|
||||
isAutoScrollPlaying = false
|
||||
showBars = true
|
||||
},
|
||||
isCollapsed = isAutoScrollCollapsed,
|
||||
onCollapseChange = { isAutoScrollCollapsed = it }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue