Position persistence during orientation change (#64)
* Fixed PDF reading position restoration during orientation change * Refactored navigation logic in `EpubReaderScreen` and `PaginatedReader` to improve position restoration and UI consistency. - Renamed `isNavigatingToBookmark` to `isNavigatingToPosition` and updated the overlay UI to reflect general position navigation. - Added an orientation change listener to restore the reading position in `VERTICAL_SCROLL` mode using the last known locator. - Improved highlight navigation in `VERTICAL_SCROLL` and `PAGINATED` modes, including chunk injection logic for highlights and fallback pagination logic. - Implemented an anchor locator mechanism in `PaginatedReader` to preserve the reading position during layout constraint changes (e.g., orientation or resizing).
This commit is contained in:
parent
843a77d0ef
commit
74e2cec415
6 changed files with 235 additions and 119 deletions
|
|
@ -69,7 +69,9 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIntent(intent)
|
if (savedInstanceState == null) {
|
||||||
|
handleIntent(intent)
|
||||||
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
platformFeaturesRepository.checkForUpdates(this@MainActivity, updateLauncher)
|
platformFeaturesRepository.checkForUpdates(this@MainActivity, updateLauncher)
|
||||||
|
|
|
||||||
|
|
@ -1235,10 +1235,8 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val bookToSync = uiState.value.recentFiles.find {
|
val uriString = _internalState.value.selectedPdfUri?.toString()
|
||||||
it.uriString == (uiState.value.selectedPdfUri?.toString()
|
?: _internalState.value.selectedEpubUri?.toString()
|
||||||
?: uiState.value.selectedEpubUri?.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
_internalState.update {
|
_internalState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
|
|
@ -1254,21 +1252,24 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
bookToSync?.let {
|
if (uriString != null) {
|
||||||
if (uiState.value.uploadingBookIds.contains(it.bookId)) {
|
viewModelScope.launch {
|
||||||
return
|
val freshBook = recentFilesRepository.getFileByUri(uriString)
|
||||||
}
|
freshBook?.let {
|
||||||
if (uiState.value.isSyncEnabled) {
|
if (uiState.value.uploadingBookIds.contains(it.bookId)) {
|
||||||
Timber.d("Book closed, triggering metadata sync for ${it.bookId}")
|
return@launch
|
||||||
uploadSingleBookMetadata(it)
|
}
|
||||||
}
|
if (uiState.value.isSyncEnabled) {
|
||||||
|
Timber.d("Book closed, triggering metadata sync for ${it.bookId}")
|
||||||
|
uploadSingleBookMetadata(it)
|
||||||
|
}
|
||||||
|
|
||||||
if (it.sourceFolderUri != null) {
|
if (it.sourceFolderUri != null) {
|
||||||
Timber.tag("FolderAnnotationSync")
|
Timber.tag("FolderAnnotationSync")
|
||||||
.d("Book closed (Folder Linked), syncing metadata and annotations to folder: ${it.bookId}")
|
.d("Book closed (Folder Linked), syncing metadata and annotations to folder: ${it.bookId}")
|
||||||
viewModelScope.launch {
|
recentFilesRepository.syncLocalMetadataToFolder(it.bookId)
|
||||||
recentFilesRepository.syncLocalMetadataToFolder(it.bookId)
|
recentFilesRepository.syncLocalAnnotationsToFolder(it.bookId)
|
||||||
recentFilesRepository.syncLocalAnnotationsToFolder(it.bookId)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1383,8 +1384,8 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
val constraints = Constraints.Builder().setRequiresBatteryNotLow(true).build()
|
val constraints = Constraints.Builder().setRequiresBatteryNotLow(true).build()
|
||||||
val syncRequest =
|
val syncRequest =
|
||||||
PeriodicWorkRequestBuilder<FolderSyncWorker>(4, TimeUnit.HOURS).setConstraints(
|
PeriodicWorkRequestBuilder<FolderSyncWorker>(4, TimeUnit.HOURS).setConstraints(
|
||||||
constraints
|
constraints
|
||||||
).build()
|
).build()
|
||||||
workManager.enqueueUniquePeriodicWork(
|
workManager.enqueueUniquePeriodicWork(
|
||||||
FolderSyncWorker.WORK_NAME, ExistingPeriodicWorkPolicy.UPDATE, syncRequest
|
FolderSyncWorker.WORK_NAME, ExistingPeriodicWorkPolicy.UPDATE, syncRequest
|
||||||
)
|
)
|
||||||
|
|
@ -2965,7 +2966,7 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
} else {
|
} else {
|
||||||
0f
|
0f
|
||||||
}
|
}
|
||||||
Timber.d("Saving PDF position locally: URI=$currentPdfUri, Page=$page")
|
Timber.tag("PdfPositionDebug").d("ViewModel: Saving to DB | Page: $page | Total: $totalPages | URI: ${currentPdfUri.lastPathSegment}")
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
recentFilesRepository.getFileByUri(currentPdfUri.toString())?.let { _ ->
|
recentFilesRepository.getFileByUri(currentPdfUri.toString())?.let { _ ->
|
||||||
recentFilesRepository.updatePdfReadingPosition(
|
recentFilesRepository.updatePdfReadingPosition(
|
||||||
|
|
@ -2973,6 +2974,8 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Timber.tag("PdfPositionDebug").w("ViewModel: Save aborted. No selectedPdfUri found in state.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
*/
|
*/
|
||||||
// EpubReaderScreen.kt
|
// EpubReaderScreen.kt
|
||||||
@file:OptIn(ExperimentalSerializationApi::class) @file:Suppress("VariableNeverRead",
|
@file:OptIn(ExperimentalSerializationApi::class) @file:Suppress("VariableNeverRead",
|
||||||
"UnusedVariable", "Unused"
|
"UnusedVariable", "Unused", "SimplifyBooleanWithConstants"
|
||||||
)
|
)
|
||||||
|
|
||||||
package com.aryan.reader.epubreader
|
package com.aryan.reader.epubreader
|
||||||
|
|
@ -409,7 +409,7 @@ fun EpubReaderHost(
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
val searchFocusRequester = remember { FocusRequester() }
|
val searchFocusRequester = remember { FocusRequester() }
|
||||||
val containerFocusRequester = remember { FocusRequester() }
|
val containerFocusRequester = remember { FocusRequester() }
|
||||||
var isNavigatingToBookmark by remember { mutableStateOf(false) }
|
var isNavigatingToPosition by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
var isPageSliderVisible by remember { mutableStateOf(false) }
|
var isPageSliderVisible by remember { mutableStateOf(false) }
|
||||||
var sliderCurrentPage by remember { mutableFloatStateOf(0f) }
|
var sliderCurrentPage by remember { mutableFloatStateOf(0f) }
|
||||||
|
|
@ -817,17 +817,22 @@ fun EpubReaderHost(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(initialLocator, initialCfi) {
|
val configuration = androidx.compose.ui.platform.LocalConfiguration.current
|
||||||
if (currentRenderMode == RenderMode.VERTICAL_SCROLL && initialLocator != null && cfiToLoad == null) {
|
var lastOrientation by remember { mutableIntStateOf(configuration.orientation) }
|
||||||
if (!initialCfi.isNullOrBlank()) {
|
|
||||||
Timber.d("V_SCROLL: Using raw initialCfi: $initialCfi")
|
LaunchedEffect(configuration.orientation) {
|
||||||
if (currentChapterIndex != initialLocator.chapterIndex) {
|
if (lastOrientation != configuration.orientation) {
|
||||||
currentChapterIndex = initialLocator.chapterIndex
|
lastOrientation = configuration.orientation
|
||||||
|
if (currentRenderMode == RenderMode.VERTICAL_SCROLL) {
|
||||||
|
lastKnownLocator?.let { locator ->
|
||||||
|
scope.launch {
|
||||||
|
val cfi = locatorConverter.getCfiFromLocator(epubBook, locator)
|
||||||
|
if (cfi != null) {
|
||||||
|
delay(300L)
|
||||||
|
webViewRefForTts?.evaluateJavascript("javascript:window.scrollToCfi('${escapeJsString(cfi)}');", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cfiToLoad = initialCfi
|
|
||||||
} else {
|
|
||||||
Timber.w("V_SCROLL: initialCfi is null or blank. Position cannot be restored from locator as conversion is disabled per request.")
|
|
||||||
isInitialCfiLoad = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1537,7 +1542,7 @@ fun EpubReaderHost(
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (targetChunk != null && targetChunk >= 0) {
|
if (targetChunk != null && targetChunk >= 0) {
|
||||||
isNavigatingToBookmark = true
|
isNavigatingToPosition = true
|
||||||
|
|
||||||
if (targetChunk >= loadedChunkCount) {
|
if (targetChunk >= loadedChunkCount) {
|
||||||
Timber.tag("BookmarkDiagnosis").d("Manual Chunk Injection: Loading from $loadedChunkCount to $targetChunk")
|
Timber.tag("BookmarkDiagnosis").d("Manual Chunk Injection: Loading from $loadedChunkCount to $targetChunk")
|
||||||
|
|
@ -1556,9 +1561,6 @@ fun EpubReaderHost(
|
||||||
loadUpToChunkIndex = targetChunk
|
loadUpToChunkIndex = targetChunk
|
||||||
loadedChunkCount = max(loadedChunkCount, targetChunk + 1)
|
loadedChunkCount = max(loadedChunkCount, targetChunk + 1)
|
||||||
} else {
|
} else {
|
||||||
// Even if loadedChunkCount is high enough in Kotlin state,
|
|
||||||
// ensure the specific chunk for the bookmark is actually in the DOM.
|
|
||||||
// (Sometimes rapid jumps might leave gaps if logic was loose)
|
|
||||||
val content = chapterChunks.getOrNull(targetChunk)
|
val content = chapterChunks.getOrNull(targetChunk)
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
val escaped = escapeJsString(content)
|
val escaped = escapeJsString(content)
|
||||||
|
|
@ -1576,8 +1578,8 @@ fun EpubReaderHost(
|
||||||
|
|
||||||
scope.launch {
|
scope.launch {
|
||||||
delay(3000)
|
delay(3000)
|
||||||
if (isNavigatingToBookmark) {
|
if (isNavigatingToPosition) {
|
||||||
isNavigatingToBookmark = false
|
isNavigatingToPosition = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1591,6 +1593,7 @@ fun EpubReaderHost(
|
||||||
}
|
}
|
||||||
RenderMode.PAGINATED -> {
|
RenderMode.PAGINATED -> {
|
||||||
Timber.d("P-Mode Click: Navigating to bookmark. Chapter: ${bookmark.chapterIndex}, CFI: '${bookmark.cfi}'")
|
Timber.d("P-Mode Click: Navigating to bookmark. Chapter: ${bookmark.chapterIndex}, CFI: '${bookmark.cfi}'")
|
||||||
|
isNavigatingToPosition = true
|
||||||
val locator = locatorConverter.getLocatorFromCfi(
|
val locator = locatorConverter.getLocatorFromCfi(
|
||||||
book = epubBook,
|
book = epubBook,
|
||||||
chapterIndex = bookmark.chapterIndex,
|
chapterIndex = bookmark.chapterIndex,
|
||||||
|
|
@ -1610,11 +1613,13 @@ fun EpubReaderHost(
|
||||||
paginatedPagerState.scrollToPage(chapterStartPage)
|
paginatedPagerState.scrollToPage(chapterStartPage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isNavigatingToPosition = false
|
||||||
} else {
|
} else {
|
||||||
Timber.w("P-Mode Click: Failed to convert CFI to Locator. Using old findPageForCfi as a fallback.")
|
Timber.w("P-Mode Click: Failed to convert CFI to Locator. Using old findPageForCfi as a fallback.")
|
||||||
paginator?.findPageForCfi(bookmark.chapterIndex, bookmark.cfi) { pageIndex ->
|
paginator?.findPageForCfi(bookmark.chapterIndex, bookmark.cfi) { pageIndex ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
paginatedPagerState.scrollToPage(pageIndex)
|
paginatedPagerState.scrollToPage(pageIndex)
|
||||||
|
isNavigatingToPosition = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1635,11 +1640,48 @@ fun EpubReaderHost(
|
||||||
val targetChunk = locator?.let { it.blockIndex / 20 }
|
val targetChunk = locator?.let { it.blockIndex / 20 }
|
||||||
|
|
||||||
if (highlight.chapterIndex != currentChapterIndex) {
|
if (highlight.chapterIndex != currentChapterIndex) {
|
||||||
chunkTargetOverride = targetChunk ?: 0
|
chunkTargetOverride = if (targetChunk != null && targetChunk >= 0) targetChunk else 0
|
||||||
currentChapterIndex = highlight.chapterIndex
|
currentChapterIndex = highlight.chapterIndex
|
||||||
} else {
|
} else {
|
||||||
if (targetChunk != null && targetChunk >= loadedChunkCount) {
|
if (targetChunk != null && targetChunk >= 0) {
|
||||||
loadUpToChunkIndex = targetChunk
|
isNavigatingToPosition = true
|
||||||
|
|
||||||
|
if (targetChunk >= loadedChunkCount) {
|
||||||
|
val chunksToInject = (loadedChunkCount..targetChunk)
|
||||||
|
chunksToInject.forEach { idx ->
|
||||||
|
val content = chapterChunks.getOrNull(idx)
|
||||||
|
if (content != null) {
|
||||||
|
val escaped = escapeJsString(content)
|
||||||
|
webViewRefForTts?.evaluateJavascript(
|
||||||
|
"javascript:window.virtualization.appendChunk($idx, '$escaped');",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadUpToChunkIndex = targetChunk
|
||||||
|
loadedChunkCount = max(loadedChunkCount, targetChunk + 1)
|
||||||
|
} else {
|
||||||
|
val content = chapterChunks.getOrNull(targetChunk)
|
||||||
|
if (content != null) {
|
||||||
|
val escaped = escapeJsString(content)
|
||||||
|
webViewRefForTts?.evaluateJavascript(
|
||||||
|
"javascript:window.virtualization.appendChunk($targetChunk, '$escaped');",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webViewRefForTts?.evaluateJavascript(
|
||||||
|
"javascript:window.scrollToCfi('${escapeJsString(highlight.cfi)}');",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
delay(3000)
|
||||||
|
if (isNavigatingToPosition) {
|
||||||
|
isNavigatingToPosition = false
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
webViewRefForTts?.evaluateJavascript(
|
webViewRefForTts?.evaluateJavascript(
|
||||||
"javascript:window.scrollToCfi('${escapeJsString(highlight.cfi)}');",
|
"javascript:window.scrollToCfi('${escapeJsString(highlight.cfi)}');",
|
||||||
|
|
@ -1649,10 +1691,26 @@ fun EpubReaderHost(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RenderMode.PAGINATED -> {
|
RenderMode.PAGINATED -> {
|
||||||
|
isNavigatingToPosition = true
|
||||||
val locator = locatorConverter.getLocatorFromCfi(epubBook, highlight.chapterIndex, highlight.cfi)
|
val locator = locatorConverter.getLocatorFromCfi(epubBook, highlight.chapterIndex, highlight.cfi)
|
||||||
if (locator != null) {
|
if (locator != null) {
|
||||||
val pageIndex = (paginator as? BookPaginator)?.findPageForLocator(locator)
|
val pageIndex = (paginator as? BookPaginator)?.findPageForLocator(locator)
|
||||||
if (pageIndex != null) paginatedPagerState.scrollToPage(pageIndex)
|
if (pageIndex != null) {
|
||||||
|
paginatedPagerState.scrollToPage(pageIndex)
|
||||||
|
} else {
|
||||||
|
val chapterStartPage = (paginator as? BookPaginator)?.chapterStartPageIndices?.get(highlight.chapterIndex)
|
||||||
|
if (chapterStartPage != null) {
|
||||||
|
paginatedPagerState.scrollToPage(chapterStartPage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isNavigatingToPosition = false
|
||||||
|
} else {
|
||||||
|
paginator?.findPageForCfi(highlight.chapterIndex, highlight.cfi) { pageIndex ->
|
||||||
|
scope.launch {
|
||||||
|
paginatedPagerState.scrollToPage(pageIndex)
|
||||||
|
isNavigatingToPosition = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2126,7 +2184,7 @@ fun EpubReaderHost(
|
||||||
},
|
},
|
||||||
onScrollFinished = { success ->
|
onScrollFinished = { success ->
|
||||||
Timber.tag("BookmarkDiagnosis").d("Scroll finished callback. Success: $success")
|
Timber.tag("BookmarkDiagnosis").d("Scroll finished callback. Success: $success")
|
||||||
isNavigatingToBookmark = false
|
isNavigatingToPosition = false
|
||||||
},
|
},
|
||||||
ttsScope = scope,
|
ttsScope = scope,
|
||||||
onTtsTextReady = { jsonString ->
|
onTtsTextReady = { jsonString ->
|
||||||
|
|
@ -3483,7 +3541,7 @@ fun EpubReaderHost(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isNavigatingToBookmark) {
|
if (isNavigatingToPosition) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -3495,7 +3553,7 @@ fun EpubReaderHost(
|
||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "Navigating to bookmark...",
|
text = "Navigating to position...",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onBackground
|
color = MaterialTheme.colorScheme.onBackground
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -530,6 +530,20 @@ fun PaginatedReaderScreen(
|
||||||
var debouncedTextAlign by remember { mutableStateOf(textAlign) }
|
var debouncedTextAlign by remember { mutableStateOf(textAlign) }
|
||||||
|
|
||||||
var anchorLocatorForReconfig by remember { mutableStateOf<Locator?>(null) }
|
var anchorLocatorForReconfig by remember { mutableStateOf<Locator?>(null) }
|
||||||
|
val currentPaginatorRef = remember { mutableStateOf<IPaginator?>(null) }
|
||||||
|
|
||||||
|
val previousConstraints = remember { arrayOf(this.constraints) }
|
||||||
|
if (previousConstraints[0] != this.constraints) {
|
||||||
|
val activePaginator = currentPaginatorRef.value
|
||||||
|
if (activePaginator is BookPaginator) {
|
||||||
|
val currentPage = pagerState.currentPage
|
||||||
|
val locator = activePaginator.getLocatorForPage(currentPage)
|
||||||
|
if (locator != null) {
|
||||||
|
anchorLocatorForReconfig = locator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previousConstraints[0] = this.constraints
|
||||||
|
}
|
||||||
|
|
||||||
val textStyle = remember(
|
val textStyle = remember(
|
||||||
baseTextStyle,
|
baseTextStyle,
|
||||||
|
|
@ -556,8 +570,6 @@ fun PaginatedReaderScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentPaginatorRef = remember { mutableStateOf<IPaginator?>(null) }
|
|
||||||
|
|
||||||
LaunchedEffect(fontSizeMultiplier, lineHeightMultiplier, fontFamily, textAlign) {
|
LaunchedEffect(fontSizeMultiplier, lineHeightMultiplier, fontFamily, textAlign) {
|
||||||
if (fontSizeMultiplier != debouncedFontSizeMult || lineHeightMultiplier != debouncedLineHeightMult || fontFamily != debouncedFontFamily || textAlign != debouncedTextAlign) {
|
if (fontSizeMultiplier != debouncedFontSizeMult || lineHeightMultiplier != debouncedLineHeightMult || fontFamily != debouncedFontFamily || textAlign != debouncedTextAlign) {
|
||||||
Timber.d("Formatting changed. Waiting for debounce.")
|
Timber.d("Formatting changed. Waiting for debounce.")
|
||||||
|
|
@ -2902,7 +2914,7 @@ private fun PaginatedTextSelectionMenu(
|
||||||
onHighlight: ((HighlightColor) -> Unit)?,
|
onHighlight: ((HighlightColor) -> Unit)?,
|
||||||
onDelete: (() -> Unit)?,
|
onDelete: (() -> Unit)?,
|
||||||
@Suppress("unused") isProUser: Boolean,
|
@Suppress("unused") isProUser: Boolean,
|
||||||
isOss: Boolean,
|
@Suppress("unused") isOss: Boolean,
|
||||||
activeHighlightPalette: List<HighlightColor> = emptyList(),
|
activeHighlightPalette: List<HighlightColor> = emptyList(),
|
||||||
onOpenPaletteManager: (() -> Unit)? = null
|
onOpenPaletteManager: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -419,6 +419,7 @@ internal fun PdfVerticalReader(
|
||||||
|
|
||||||
state.snapToPageHandler = { index ->
|
state.snapToPageHandler = { index ->
|
||||||
val clampedPanY = calculateTargetPanY(index)
|
val clampedPanY = calculateTargetPanY(index)
|
||||||
|
Timber.tag("PdfPositionDebug").d("VerticalReader: snapToPage($index) called. ClampedPanY: $clampedPanY")
|
||||||
if (clampedPanY != null) {
|
if (clampedPanY != null) {
|
||||||
panYAnimatable.snapTo(clampedPanY)
|
panYAnimatable.snapTo(clampedPanY)
|
||||||
}
|
}
|
||||||
|
|
@ -1210,9 +1211,7 @@ internal fun PdfVerticalReader(
|
||||||
|
|
||||||
LaunchedEffect(visiblePages, screenHeight) {
|
LaunchedEffect(visiblePages, screenHeight) {
|
||||||
snapshotFlow {
|
snapshotFlow {
|
||||||
Pair(
|
Pair(panYAnimatable.value, zoomAnimatable.value)
|
||||||
panYAnimatable.value, zoomAnimatable.value
|
|
||||||
)
|
|
||||||
}.collectLatest { (panY, zoom) ->
|
}.collectLatest { (panY, zoom) ->
|
||||||
if (visiblePages.isNotEmpty()) {
|
if (visiblePages.isNotEmpty()) {
|
||||||
state.firstVisiblePage = visiblePages.first().index
|
state.firstVisiblePage = visiblePages.first().index
|
||||||
|
|
@ -1228,6 +1227,7 @@ internal fun PdfVerticalReader(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mostVisible != null && mostVisible.index != state.currentPage) {
|
if (mostVisible != null && mostVisible.index != state.currentPage) {
|
||||||
|
Timber.tag("PdfPositionDebug").v("VerticalReader: Page changed to ${mostVisible.index} (PanY: $panY)")
|
||||||
state.currentPage = mostVisible.index
|
state.currentPage = mostVisible.index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,6 @@ import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.LifecycleEventObserver
|
|
||||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
|
@ -167,6 +162,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.BiasAlignment
|
import androidx.compose.ui.BiasAlignment
|
||||||
|
|
@ -228,7 +224,10 @@ import androidx.core.graphics.createBitmap
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
|
|
@ -283,6 +282,8 @@ import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.conflate
|
import kotlinx.coroutines.flow.conflate
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
@ -708,9 +709,10 @@ fun PdfViewerScreen(
|
||||||
var displayMode by remember { mutableStateOf(loadDisplayMode(context)) }
|
var displayMode by remember { mutableStateOf(loadDisplayMode(context)) }
|
||||||
var isPdfDarkMode by remember { mutableStateOf(loadPdfDarkMode(context)) }
|
var isPdfDarkMode by remember { mutableStateOf(loadPdfDarkMode(context)) }
|
||||||
var pageAspectRatios by remember { mutableStateOf<List<Float>>(emptyList()) }
|
var pageAspectRatios by remember { mutableStateOf<List<Float>>(emptyList()) }
|
||||||
var showBars by remember { mutableStateOf(true) }
|
var showBars by rememberSaveable { mutableStateOf(true) }
|
||||||
var isFullScreen by remember { mutableStateOf(false) }
|
var isFullScreen by remember { mutableStateOf(false) }
|
||||||
var documentPassword by remember { mutableStateOf<String?>(null) }
|
var documentPassword by rememberSaveable { mutableStateOf<String?>(null) }
|
||||||
|
var pendingRestorePage by rememberSaveable { mutableStateOf(initialPage) }
|
||||||
var isScrollLocked by remember { mutableStateOf(false) }
|
var isScrollLocked by remember { mutableStateOf(false) }
|
||||||
var showPasswordDialog by remember { mutableStateOf(false) }
|
var showPasswordDialog by remember { mutableStateOf(false) }
|
||||||
var isPasswordError by remember { mutableStateOf(false) }
|
var isPasswordError by remember { mutableStateOf(false) }
|
||||||
|
|
@ -761,6 +763,7 @@ fun PdfViewerScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
var isDockDragging by remember { mutableStateOf(false) }
|
var isDockDragging by remember { mutableStateOf(false) }
|
||||||
|
var initialScrollDone by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
var isAutoScrollModeActive by remember { mutableStateOf(false) }
|
var isAutoScrollModeActive by remember { mutableStateOf(false) }
|
||||||
var isAutoScrollPlaying by remember { mutableStateOf(false) }
|
var isAutoScrollPlaying by remember { mutableStateOf(false) }
|
||||||
|
|
@ -810,11 +813,9 @@ fun PdfViewerScreen(
|
||||||
var showZoomIndicator by remember { mutableStateOf(false) }
|
var showZoomIndicator by remember { mutableStateOf(false) }
|
||||||
var bookmarks by remember(pdfUri) { mutableStateOf(loadPdfBookmarksFromJson(initialBookmarksJson)) }
|
var bookmarks by remember(pdfUri) { mutableStateOf(loadPdfBookmarksFromJson(initialBookmarksJson)) }
|
||||||
|
|
||||||
var showPenPlayground by remember { mutableStateOf(false) }
|
var showPenPlayground by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var isEditMode by rememberSaveable { mutableStateOf(false) }
|
||||||
var isEditMode by remember { mutableStateOf(false) }
|
var isDockMinimized by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
var isDockMinimized by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
val isDrawingActive by remember(isEditMode, isDockMinimized) {
|
val isDrawingActive by remember(isEditMode, isDockMinimized) {
|
||||||
derivedStateOf { isEditMode && !isDockMinimized }
|
derivedStateOf { isEditMode && !isDockMinimized }
|
||||||
|
|
@ -988,9 +989,7 @@ fun PdfViewerScreen(
|
||||||
|
|
||||||
val annotationSettingsRepo = remember(context) { AnnotationSettingsRepository(context) }
|
val annotationSettingsRepo = remember(context) { AnnotationSettingsRepository(context) }
|
||||||
val toolSettings by annotationSettingsRepo.settings.collectAsState()
|
val toolSettings by annotationSettingsRepo.settings.collectAsState()
|
||||||
|
var showToolSettings by rememberSaveable { mutableStateOf(false) }
|
||||||
var showToolSettings by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
val isHighlighterSnapEnabled = toolSettings.isHighlighterSnapEnabled
|
val isHighlighterSnapEnabled = toolSettings.isHighlighterSnapEnabled
|
||||||
|
|
||||||
val selectedTool = toolSettings.getActiveTool()
|
val selectedTool = toolSettings.getActiveTool()
|
||||||
|
|
@ -1051,7 +1050,7 @@ fun PdfViewerScreen(
|
||||||
var totalPages by remember { mutableIntStateOf(0) }
|
var totalPages by remember { mutableIntStateOf(0) }
|
||||||
var currentPageScale by remember { mutableFloatStateOf(1f) }
|
var currentPageScale by remember { mutableFloatStateOf(1f) }
|
||||||
val textBoxes = remember { mutableStateListOf<PdfTextBox>() }
|
val textBoxes = remember { mutableStateListOf<PdfTextBox>() }
|
||||||
var selectedTextBoxId by remember { mutableStateOf<String?>(null) }
|
var selectedTextBoxId by rememberSaveable { mutableStateOf<String?>(null) }
|
||||||
val userHighlights = remember { mutableStateListOf<PdfUserHighlight>() }
|
val userHighlights = remember { mutableStateListOf<PdfUserHighlight>() }
|
||||||
val drawingState = remember { PdfDrawingState() }
|
val drawingState = remember { PdfDrawingState() }
|
||||||
val pdfiumCore = remember(context) { PdfiumCoreKt(Dispatchers.Default) }
|
val pdfiumCore = remember(context) { PdfiumCoreKt(Dispatchers.Default) }
|
||||||
|
|
@ -1070,13 +1069,21 @@ fun PdfViewerScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
|
|
||||||
val saveMutex = remember { Mutex() }
|
|
||||||
|
|
||||||
var initialScrollDone by remember { mutableStateOf(false) }
|
|
||||||
var isDocumentReady by remember { mutableStateOf(false) }
|
var isDocumentReady by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val lastSavedHashes = remember(currentBookId) { IntArray(5) { 0 } }
|
LaunchedEffect(currentPage, isDocumentReady, totalPages, initialScrollDone) {
|
||||||
|
if (isDocumentReady && totalPages > 0) {
|
||||||
|
if (initialScrollDone) {
|
||||||
|
Timber.tag("PdfPositionDebug").v("UI: Tracking | currentPage: $currentPage | pendingRestorePage updated")
|
||||||
|
pendingRestorePage = currentPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
val saveMutex = remember { Mutex() }
|
||||||
|
|
||||||
|
val lastSavedHashes = remember(currentBookId) { IntArray(5) { -1 } }
|
||||||
|
|
||||||
val currentAnnotations by rememberUpdatedState(allAnnotations)
|
val currentAnnotations by rememberUpdatedState(allAnnotations)
|
||||||
val currentTextBoxes by rememberUpdatedState(textBoxes.toList())
|
val currentTextBoxes by rememberUpdatedState(textBoxes.toList())
|
||||||
|
|
@ -1084,24 +1091,39 @@ fun PdfViewerScreen(
|
||||||
val currentBookmarks by rememberUpdatedState(bookmarks)
|
val currentBookmarks by rememberUpdatedState(bookmarks)
|
||||||
val currentTotalPages by rememberUpdatedState(totalDisplayPages)
|
val currentTotalPages by rememberUpdatedState(totalDisplayPages)
|
||||||
val currentPageState by rememberUpdatedState(currentPage)
|
val currentPageState by rememberUpdatedState(currentPage)
|
||||||
|
val currentPendingPage by rememberUpdatedState(pendingRestorePage)
|
||||||
|
|
||||||
val saveAllData = remember(currentBookId, annotationRepository, textBoxRepository, highlightRepository) {
|
val saveAllData = remember(currentBookId, annotationRepository, textBoxRepository, highlightRepository) {
|
||||||
{ force: Boolean ->
|
{ force: Boolean ->
|
||||||
coroutineScope.launch {
|
viewModel.viewModelScope.launch {
|
||||||
val bookId = currentBookId ?: return@launch
|
val bookId = currentBookId ?: return@launch
|
||||||
|
|
||||||
|
if (!isDocumentReady && !force) {
|
||||||
|
Timber.tag("PdfPositionDebug").w("UI: Save ignored. Document not ready.")
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
val annots = currentAnnotations
|
val annots = currentAnnotations
|
||||||
val boxes = currentTextBoxes
|
val boxes = currentTextBoxes
|
||||||
val highlights = currentHighlights
|
val highlights = currentHighlights
|
||||||
val bms = currentBookmarks
|
val bms = currentBookmarks
|
||||||
val page = currentPageState
|
|
||||||
val totalPgs = currentTotalPages
|
val totalPgs = currentTotalPages
|
||||||
|
|
||||||
|
val restoreTarget = currentPendingPage ?: 0
|
||||||
|
val page = if (!initialScrollDone) {
|
||||||
|
Timber.tag("PdfPositionDebug").i("UI: Save during restoration | Using restoreTarget: $restoreTarget (CurrentUI: $currentPageState)")
|
||||||
|
restoreTarget
|
||||||
|
} else {
|
||||||
|
currentPageState
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.tag("PdfPositionDebug").v("UI: Save logic | Choosing: $page (UI: $currentPageState, Target: $restoreTarget, Done: $initialScrollDone)")
|
||||||
|
|
||||||
val annotsHash = annots.hashCode()
|
val annotsHash = annots.hashCode()
|
||||||
val boxesHash = boxes.hashCode()
|
val boxesHash = boxes.hashCode()
|
||||||
val highlightsHash = highlights.hashCode()
|
val highlightsHash = highlights.hashCode()
|
||||||
val bmsHash = bms.hashCode()
|
val bmsHash = bms.hashCode()
|
||||||
|
|
||||||
// Protect the lock and I/O execution with NonCancellable
|
|
||||||
withContext(NonCancellable) {
|
withContext(NonCancellable) {
|
||||||
saveMutex.withLock {
|
saveMutex.withLock {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
|
@ -1131,13 +1153,13 @@ fun PdfViewerScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val bookmarksJson = JSONArray(objectList).toString()
|
val bookmarksJson = JSONArray(objectList).toString()
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) { onBookmarksChanged(bookmarksJson) }
|
||||||
onBookmarksChanged(bookmarksJson)
|
|
||||||
}
|
|
||||||
lastSavedHashes[3] = bmsHash
|
lastSavedHashes[3] = bmsHash
|
||||||
didSave = true
|
didSave = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (force || page != lastSavedHashes[4]) {
|
if (force || page != lastSavedHashes[4]) {
|
||||||
|
Timber.tag("PdfPositionDebug").d("UI: COMMIT SAVE | Page: $page | Total: $totalPgs | Force: $force")
|
||||||
if (totalPgs > 0) {
|
if (totalPgs > 0) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
onSavePosition(page, totalPgs)
|
onSavePosition(page, totalPgs)
|
||||||
|
|
@ -1145,10 +1167,6 @@ fun PdfViewerScreen(
|
||||||
}
|
}
|
||||||
lastSavedHashes[4] = page
|
lastSavedHashes[4] = page
|
||||||
}
|
}
|
||||||
|
|
||||||
if (didSave) {
|
|
||||||
Timber.tag("PdfSavePerf").d("Saved data for book $bookId")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1159,12 +1177,17 @@ fun PdfViewerScreen(
|
||||||
DisposableEffect(lifecycleOwner) {
|
DisposableEffect(lifecycleOwner) {
|
||||||
val observer = LifecycleEventObserver { _, event ->
|
val observer = LifecycleEventObserver { _, event ->
|
||||||
if (event == Lifecycle.Event.ON_PAUSE || event == Lifecycle.Event.ON_STOP) {
|
if (event == Lifecycle.Event.ON_PAUSE || event == Lifecycle.Event.ON_STOP) {
|
||||||
Timber.tag("PdfSavePerf").i("Lifecycle $event triggered, forcing save.")
|
val shouldSave = initialScrollDone || (currentPageState != 0)
|
||||||
coroutineScope.launch {
|
|
||||||
if (richTextController != null) {
|
if (shouldSave) {
|
||||||
withContext(NonCancellable) { richTextController.saveImmediate() }
|
viewModel.viewModelScope.launch {
|
||||||
|
if (richTextController != null) {
|
||||||
|
withContext(NonCancellable) { richTextController.saveImmediate() }
|
||||||
|
}
|
||||||
|
saveAllData(true).join()
|
||||||
}
|
}
|
||||||
saveAllData(true).join()
|
} else {
|
||||||
|
Timber.tag("PdfPositionDebug").w("Lifecycle $event triggered: skipping save (initial settling).")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1735,35 +1758,44 @@ fun PdfViewerScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(isDocumentReady) {
|
LaunchedEffect(isDocumentReady, totalDisplayPages, displayMode) {
|
||||||
if (isDocumentReady && !initialScrollDone) {
|
if (isDocumentReady && !initialScrollDone) {
|
||||||
val pageCount = pagerState.pageCount
|
val pageCount = totalDisplayPages
|
||||||
if (pageCount > 0) {
|
if (pageCount <= 0) return@LaunchedEffect
|
||||||
val targetPage = initialPage?.coerceIn(0, pageCount - 1) ?: 0
|
|
||||||
Timber.d("Initial Setup: Document is ready. Target page: $targetPage.")
|
|
||||||
|
|
||||||
coroutineScope.launch {
|
val targetPage = pendingRestorePage?.coerceIn(0, pageCount - 1) ?: 0
|
||||||
if (pagerState.currentPage != targetPage) {
|
Timber.tag("PdfPositionDebug").i("UI: Restoration Start | Target: $targetPage | Mode: $displayMode | Total: $pageCount")
|
||||||
when (displayMode) {
|
|
||||||
DisplayMode.PAGINATION -> {
|
|
||||||
Timber.d("Initial Setup: Animating scroll to page $targetPage.")
|
|
||||||
pagerState.scrollToPage(targetPage)
|
|
||||||
}
|
|
||||||
|
|
||||||
DisplayMode.VERTICAL_SCROLL -> {
|
try {
|
||||||
Timber.d("Initial Setup: Snapping scroll to item $targetPage.")
|
delay(200)
|
||||||
verticalReaderState.snapToPage(targetPage)
|
|
||||||
pagerState.scrollToPage(targetPage)
|
when (displayMode) {
|
||||||
}
|
DisplayMode.PAGINATION -> {
|
||||||
|
if (pagerState.currentPage != targetPage) {
|
||||||
|
pagerState.scrollToPage(targetPage)
|
||||||
}
|
}
|
||||||
|
initialScrollDone = true
|
||||||
|
}
|
||||||
|
DisplayMode.VERTICAL_SCROLL -> {
|
||||||
|
var retries = 0
|
||||||
|
while (verticalReaderState.snapToPageHandler == null && retries < 20) {
|
||||||
|
delay(50)
|
||||||
|
retries++
|
||||||
|
}
|
||||||
|
Timber.tag("PdfPositionDebug").d("UI: Executing Vertical snapToPage($targetPage) after $retries retries")
|
||||||
|
verticalReaderState.snapToPage(targetPage)
|
||||||
|
delay(100)
|
||||||
|
initialScrollDone = true
|
||||||
}
|
}
|
||||||
initialScrollDone = true
|
|
||||||
Timber.d("Initial Setup: Scroll complete. Page saving is now enabled.")
|
|
||||||
}
|
}
|
||||||
} else {
|
Timber.tag("PdfPositionDebug").i("UI: Restoration Complete | Now at Page: $currentPage")
|
||||||
Timber.w(
|
} catch (e: Exception) {
|
||||||
"Initial Setup: Document is ready, but pager pageCount is still 0. This may happen on rapid open/close. Scroll will be skipped."
|
if (e is kotlinx.coroutines.CancellationException) {
|
||||||
)
|
Timber.tag("PdfPositionDebug").w("UI: Restoration cancelled (likely new recomposition)")
|
||||||
|
} else {
|
||||||
|
Timber.tag("PdfPositionDebug").e(e, "UI: Restoration error.")
|
||||||
|
initialScrollDone = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2032,18 +2064,21 @@ fun PdfViewerScreen(
|
||||||
} else {
|
} else {
|
||||||
ttsController.stop()
|
ttsController.stop()
|
||||||
|
|
||||||
coroutineScope.launch {
|
viewModel.viewModelScope.launch {
|
||||||
|
initialScrollDone = true
|
||||||
|
|
||||||
if (richTextController != null) {
|
if (richTextController != null) {
|
||||||
withContext(NonCancellable) {
|
withContext(NonCancellable) {
|
||||||
Timber.tag("RichTextFlow").d("Forcing RichTextController immediate sync and save...")
|
|
||||||
richTextController.saveImmediate()
|
richTextController.saveImmediate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAllData(true).join()
|
saveAllData(true).join()
|
||||||
|
|
||||||
Timber.tag("AnnotationSync").d("Save complete. Navigating back.")
|
withContext(Dispatchers.Main) {
|
||||||
onNavigateBack()
|
Timber.tag("PdfPositionDebug").d("Exit save complete. Navigating back.")
|
||||||
|
onNavigateBack()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5006,16 +5041,22 @@ fun PdfViewerScreen(
|
||||||
}
|
}
|
||||||
saveAllData(true).join()
|
saveAllData(true).join()
|
||||||
|
|
||||||
|
val resolvedPage = if (!initialScrollDone && currentPage == 0) {
|
||||||
|
pendingRestorePage ?: 0
|
||||||
|
} else {
|
||||||
|
currentPage
|
||||||
|
}
|
||||||
|
|
||||||
if (hasReflowFile) {
|
if (hasReflowFile) {
|
||||||
val item = uiState.allRecentFiles.find { it.bookId == reflowBookId }
|
val item = uiState.allRecentFiles.find { it.bookId == reflowBookId }
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
viewModel.switchToFileSeamlessly(item, currentPage)
|
viewModel.switchToFileSeamlessly(item, resolvedPage)
|
||||||
} else {
|
} else {
|
||||||
viewModel.generateAndImportReflowFile(
|
viewModel.generateAndImportReflowFile(
|
||||||
pdfBookId = bookId,
|
pdfBookId = bookId,
|
||||||
pdfUri = pdfUri,
|
pdfUri = pdfUri,
|
||||||
originalTitle = originalFileName,
|
originalTitle = originalFileName,
|
||||||
autoOpenPage = currentPage
|
autoOpenPage = resolvedPage
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -5023,7 +5064,7 @@ fun PdfViewerScreen(
|
||||||
pdfBookId = bookId,
|
pdfBookId = bookId,
|
||||||
pdfUri = pdfUri,
|
pdfUri = pdfUri,
|
||||||
originalTitle = originalFileName,
|
originalTitle = originalFileName,
|
||||||
autoOpenPage = currentPage
|
autoOpenPage = resolvedPage
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue