V1.0.46 oss (#253)
* add a UI trigger for demo annotations * Implement internal link navigation * Simplify bottom padding logic in `EpubReaderScreen` for vertical scroll mode * Add support for Calibre metadata * Implement advanced library management with Room-backed collections, tags, and smart rules. * Display book series information in book details and remove tags from home screen * Implement zoom reset functionality in PDF viewer * Add a "Pages" thumbnail view to the PDF navigation drawer * Implement jump-back navigation in PDF viewer * MainViewModel: disable FolderSyncWorker, add log export, and enhance PDF position logging * Replace Gson with Kotlin Serialization in SmartCollectionEngine * Implement ONNX-based speech bubble detection * Implement speech bubble detection in PDF viewer * Implement "Smart Comic Zoom" feature for PDF manga/comic reading * Implement reader session persistence and restoration in `MainViewModel` * Add tap-to-turn page feature to PDF viewer * Improve book cache management and recovery * Refactor top overlay padding logic in `PdfViewerScreen` * Implement stylus eraser support in PDF reader * Refactor ReaderTextFormatPanel to use ModalBottomSheet and add new formatting controls * Introduce a customizable horizontal margin setting for the EPUB reader * Refine folder sync and metadata handling for better conflict resolution and stability. * Introduce user-adjustable image scaling for the EPUB reader * Use maxWidthPx as default width fallback for blocks * Improve cross-page text selection and header styling in the paginated reader * Refactor speech bubble detection and UI to support segmentation masks and interactive scaling * Improve speech bubble detection masking and rendering quality * Update ONNX speech bubble detector to use `.ort` model and optimize inference * Improve pagination accuracy * Implement hierarchical folder navigation for the library. * Refactor library item layout for improved space efficiency * Add support for browsing and downloading Google Fonts * Persist library landing state and add shelf search functionality * Fix base tts sample. * Move SpeechBubbleDetector to main source set and update ONNX dependency * Implement a "locate" feature and improve synchronization for TTS (Text-to-Speech) playback across EPUB and PDF readers. * Refine TTS synchronization and voice management in the EPUB reader * Adjust OCR checks for OSS flavor and optimize external dictionary intent flags * Add "Locate" button to PDF drawer's pages tab and move the page number to bottom right of thumbnail * fix various crashes * Refactor SpeechBubbleDetector into product flavors * Implement speech bubble detection caching and background prefetching * Implement on-demand download for Bubble Zoom ML model * Add smooth animation for PDF speech bubble expansion * fixes #252 * hide Google Fonts option, in FontsScreen, for offline variant * Bump version to 1.0.46(46)
This commit is contained in:
parent
579b6f25bf
commit
d16bdd70d4
67 changed files with 9724 additions and 1890 deletions
|
|
@ -92,6 +92,8 @@ import java.io.BufferedReader
|
|||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStreamReader
|
||||
|
||||
private const val TAG_LINK_NAV = "LINK_NAV"
|
||||
|
||||
private fun getFontCssInjection(): String {
|
||||
return """
|
||||
@font-face { font-family: 'Merriweather'; src: url('file:///android_asset/fonts/merriweather.ttf'); }
|
||||
|
|
@ -313,6 +315,17 @@ class FootnoteJsBridge(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
class LinkNavJsBridge(
|
||||
private val currentChapterTitle: String
|
||||
) {
|
||||
@JavascriptInterface
|
||||
fun onLinkClicked(href: String, epubType: String, linkText: String) {
|
||||
Timber.tag(TAG_LINK_NAV)
|
||||
.d("[JS-CLICK] href='$href', epub:type='$epubType', label='$linkText' | currentChapter='$currentChapterTitle'")
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@Composable
|
||||
fun ChapterWebView(
|
||||
|
|
@ -336,6 +349,8 @@ fun ChapterWebView(
|
|||
currentFontSize: Float,
|
||||
currentLineHeight: Float,
|
||||
currentParagraphGap: Float,
|
||||
currentImageSize: Float,
|
||||
currentHorizontalMargin: Float,
|
||||
onChapterInitiallyScrolled: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onTap: () -> Unit,
|
||||
|
|
@ -370,6 +385,7 @@ fun ChapterWebView(
|
|||
onAutoScrollChapterEnd: () -> Unit = {},
|
||||
activeHighlightPalette: List<HighlightColor>,
|
||||
onUpdatePalette: (Int, HighlightColor) -> Unit,
|
||||
onInternalLinkClick: (String) -> Unit,
|
||||
activeTextureId: String? = null
|
||||
) {
|
||||
Timber.d(
|
||||
|
|
@ -471,6 +487,8 @@ fun ChapterWebView(
|
|||
currentFontSize,
|
||||
currentLineHeight,
|
||||
currentParagraphGap,
|
||||
currentImageSize,
|
||||
currentHorizontalMargin,
|
||||
currentFontFamily,
|
||||
currentTextAlign
|
||||
) {
|
||||
|
|
@ -550,6 +568,11 @@ fun ChapterWebView(
|
|||
consoleMessage?.let {
|
||||
val message = it.message()
|
||||
when {
|
||||
message.startsWith("LINK_NAV:") -> {
|
||||
Timber.tag(TAG_LINK_NAV)
|
||||
.d("JS -> ${message.substringAfter("LINK_NAV: ")}")
|
||||
}
|
||||
|
||||
message.startsWith("FootnoteDiag:") -> {
|
||||
Timber.tag("FootnoteDiag")
|
||||
.d("JS -> ${message.substringAfter("FootnoteDiag: ")}")
|
||||
|
|
@ -653,16 +676,31 @@ fun ChapterWebView(
|
|||
}, "FootnoteBridge"
|
||||
)
|
||||
|
||||
addJavascriptInterface(
|
||||
LinkNavJsBridge(chapterTitle), "LinkNavBridge"
|
||||
)
|
||||
|
||||
webViewClient = object : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?, request: WebResourceRequest?
|
||||
): Boolean {
|
||||
val url = request?.url?.toString()
|
||||
if (url != null && (url.startsWith("http://") || url.startsWith("https://"))) {
|
||||
Timber.d("Intercepted external link: $url")
|
||||
Timber.tag(TAG_LINK_NAV)
|
||||
.d("[EXTERNAL-INTERCEPT] url='$url' from chapter '$chapterTitle'")
|
||||
showExternalLinkDialog = url
|
||||
return true
|
||||
}
|
||||
if (url != null && url.startsWith("file://")) {
|
||||
Timber.tag(TAG_LINK_NAV)
|
||||
.d("[INTERNAL-LINK-INTERCEPTED] url='$url' from chapter '$chapterTitle'")
|
||||
onInternalLinkClick(url)
|
||||
return true
|
||||
}
|
||||
if (url != null) {
|
||||
Timber.tag(TAG_LINK_NAV)
|
||||
.d("[INTERNAL-LINK-PASSED] url='$url' from chapter '$chapterTitle' — allowing WebView to handle")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -744,7 +782,7 @@ fun ChapterWebView(
|
|||
}
|
||||
|
||||
view?.evaluateJavascript(
|
||||
"javascript:window.updateReaderStyles($currentFontSize, $currentLineHeight, '$fontNameForJs', '${currentTextAlign.cssValue}', $currentParagraphGap);",
|
||||
"javascript:window.updateReaderStyles($currentFontSize, $currentLineHeight, '$fontNameForJs', '${currentTextAlign.cssValue}', $currentParagraphGap, $currentImageSize, $currentHorizontalMargin);",
|
||||
null
|
||||
)
|
||||
|
||||
|
|
@ -768,10 +806,59 @@ fun ChapterWebView(
|
|||
}
|
||||
} else if (!initialFragmentId.isNullOrBlank()) {
|
||||
Timber.tag("NavDiag").d("WebView onPageFinished: Scrolling to Element ID: $initialFragmentId")
|
||||
view?.evaluateJavascript(
|
||||
"javascript:var el = document.getElementById('$initialFragmentId'); if(el) { el.scrollIntoView(); } else { console.log('Element not found: $initialFragmentId'); }",
|
||||
null
|
||||
)
|
||||
val js = """
|
||||
(function() {
|
||||
var targetId = '$initialFragmentId';
|
||||
var el = document.getElementById(targetId) || document.querySelector('[name="' + targetId + '"]');
|
||||
if (el) {
|
||||
var targetScrollY = window.scrollY + el.getBoundingClientRect().top - (window.VIEWPORT_PADDING_TOP + 10);
|
||||
window.scrollTo({ top: targetScrollY, behavior: 'auto' });
|
||||
return -2;
|
||||
}
|
||||
if (window.virtualization && window.virtualization.chunksData) {
|
||||
for (var i = 0; i < window.virtualization.chunksData.length; i++) {
|
||||
var chunkHtml = window.virtualization.chunksData[i];
|
||||
if (chunkHtml && (chunkHtml.indexOf('id="' + targetId + '"') !== -1 || chunkHtml.indexOf('name="' + targetId + '"') !== -1 || chunkHtml.indexOf("id='" + targetId + "'") !== -1 || chunkHtml.indexOf("name='" + targetId + "'") !== -1)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
})()
|
||||
""".trimIndent()
|
||||
|
||||
view?.evaluateJavascript(js) { result ->
|
||||
val chunkIdx = result?.toIntOrNull() ?: -1
|
||||
if (chunkIdx >= 0) {
|
||||
for (i in 0..chunkIdx) {
|
||||
onChunkRequested(i)
|
||||
}
|
||||
val scrollJs = """
|
||||
(function() {
|
||||
var chunkIndex = $chunkIdx;
|
||||
var fragmentId = '$initialFragmentId';
|
||||
setTimeout(function() {
|
||||
var chunkDiv = document.querySelector('.chunk-container[data-chunk-index="' + chunkIndex + '"]');
|
||||
if (chunkDiv && chunkDiv.innerHTML === "" && window.virtualization && window.virtualization.chunksData[chunkIndex]) {
|
||||
chunkDiv.innerHTML = window.virtualization.chunksData[chunkIndex];
|
||||
chunkDiv.style.height = "";
|
||||
}
|
||||
setTimeout(function() {
|
||||
var el = document.getElementById(fragmentId) || document.querySelector('[name="' + fragmentId + '"]');
|
||||
if (el) {
|
||||
var targetScrollY = window.scrollY + el.getBoundingClientRect().top - (window.VIEWPORT_PADDING_TOP + 10);
|
||||
window.scrollTo({ top: targetScrollY, behavior: 'auto' });
|
||||
} else if (chunkDiv) {
|
||||
var targetScrollY = window.scrollY + chunkDiv.getBoundingClientRect().top - window.VIEWPORT_PADDING_TOP;
|
||||
window.scrollTo({ top: targetScrollY, behavior: 'auto' });
|
||||
}
|
||||
}, 50);
|
||||
}, 200);
|
||||
})()
|
||||
""".trimIndent()
|
||||
view.evaluateJavascript(scrollJs, null)
|
||||
}
|
||||
}
|
||||
onChapterInitiallyScrolled()
|
||||
scrollActionTaken = true
|
||||
} else if (initialScrollTarget != null) {
|
||||
|
|
@ -859,7 +946,7 @@ fun ChapterWebView(
|
|||
)
|
||||
|
||||
webView.evaluateJavascript(
|
||||
"javascript:window.updateReaderStyles($currentFontSize, $currentLineHeight, '$fontNameForJs', '${currentTextAlign.cssValue}', $currentParagraphGap);",
|
||||
"javascript:window.updateReaderStyles($currentFontSize, $currentLineHeight, '$fontNameForJs', '${currentTextAlign.cssValue}', $currentParagraphGap, $currentImageSize, $currentHorizontalMargin);",
|
||||
null
|
||||
)
|
||||
|
||||
|
|
@ -1075,4 +1162,4 @@ fun ChapterWebView(
|
|||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue