diff --git a/app/src/main/java/com/aryan/reader/LibraryScreen.kt b/app/src/main/java/com/aryan/reader/LibraryScreen.kt index b8c57ee..0210942 100644 --- a/app/src/main/java/com/aryan/reader/LibraryScreen.kt +++ b/app/src/main/java/com/aryan/reader/LibraryScreen.kt @@ -1334,8 +1334,8 @@ private fun FolderSyncScreen( if (syncedFolderUri == null) { EmptyState( - title = "Auto-Sync a Folder", - message = "Select a folder on your device. Episteme will automatically find and import any new books you add to it. A great way to keep your library in sync with your downloads folder!", + title = "Import files from a Folder", + message = "Select a folder on your device. Episteme will automatically find and import any new books you add to it.", onSelectFileClick = onSelectFolderClick, modifier = Modifier.fillMaxSize() ) diff --git a/app/src/main/java/com/aryan/reader/pdf/PdfVerticalReader.kt b/app/src/main/java/com/aryan/reader/pdf/PdfVerticalReader.kt index 8fbee8a..8177253 100644 --- a/app/src/main/java/com/aryan/reader/pdf/PdfVerticalReader.kt +++ b/app/src/main/java/com/aryan/reader/pdf/PdfVerticalReader.kt @@ -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(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") } diff --git a/app/src/main/java/com/aryan/reader/pdf/PdfViewerScreen.kt b/app/src/main/java/com/aryan/reader/pdf/PdfViewerScreen.kt index b231c7d..bd44a7a 100644 --- a/app/src/main/java/com/aryan/reader/pdf/PdfViewerScreen.kt +++ b/app/src/main/java/com/aryan/reader/pdf/PdfViewerScreen.kt @@ -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(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(null) } - var isDockDragging by remember { mutableStateOf(false) } - - var paginationDraggingBoxId by remember { mutableStateOf(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 } + ) + } } } }