App themes (#217)

* Updated `.gitattributes` to correctly vendor all files within the `libmobi` and `woff2` directories.

* Improved TTS chapter transition handling

* Improved TTS stability and chapter advancement in the EPUB reader.

* Implemented PDF metadata extraction for titles and authors using PdfiumCore in `MainViewModel` and `MetadataExtractionWorker`.

* Implemented support for interactive footnotes in the EPUB vertical reader.

* Added support for footnotes in the paginated EPUB reader.

* Implemented a customizable app theming system.

* Added support for app-wide contrast and text brightness customization in the "App Theme" option.

* option to adjust pull-to-chapter-change drag in the EPUB vertical reader under visual options.

* Updated `PdfViewerScreen` to replace the standalone full-screen toggle with a more comprehensive "Visual Options" system UI management tool.
This commit is contained in:
Aryan 2026-04-21 18:11:55 +05:30 committed by GitHub
parent 4cb25e6abd
commit 71e3614ad6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 1413 additions and 287 deletions

View file

@ -126,20 +126,21 @@ class AutoScrollJsBridge(
}
}
@Suppress("unused") // function used by JavaScript
@Suppress("unused")
class TtsJsBridge(
private val scope: CoroutineScope,
private val ttsStructuredTextHandler: suspend (String) -> Unit
) {
@JavascriptInterface
fun onStructuredTextExtracted(json: String) {
Timber.tag("TTS_LIST_DIAG").d("Bridge received JSON: $json")
Timber.tag("TTS_CHAPTER_CHANGE_DIAG").d("JS Bridge received JSON. Length: ${json.length}")
if (json.isNotBlank() && json != "[]") {
scope.launch {
scope.launch(kotlinx.coroutines.Dispatchers.Default) {
ttsStructuredTextHandler(json)
}
} else {
scope.launch {
Timber.tag("TTS_CHAPTER_CHANGE_DIAG").w("JS Bridge received empty or blank JSON. This may trigger a chapter skip.")
scope.launch(kotlinx.coroutines.Dispatchers.Default) {
ttsStructuredTextHandler("[]")
}
}
@ -301,6 +302,17 @@ class AiJsBridge(
}
}
@Suppress("unused")
class FootnoteJsBridge(
private val onFootnoteRequestCallback: (String) -> Unit
) {
@JavascriptInterface
fun onFootnoteRequested(htmlContent: String) {
Timber.tag("FootnoteDiag").d("Kotlin Bridge received footnote content. Length: ${htmlContent.length}")
onFootnoteRequestCallback(htmlContent)
}
}
@SuppressLint("SetJavaScriptEnabled")
@Composable
fun ChapterWebView(
@ -350,6 +362,7 @@ fun ChapterWebView(
onSearch: (String) -> Unit,
onNoteRequested: (String?) -> Unit,
onContentReadyForSummarization: suspend (String) -> Unit,
onFootnoteRequested: (String) -> Unit,
currentFontFamily: ReaderFont,
customFontPath: String? = null,
currentTextAlign: ReaderTextAlign,
@ -537,6 +550,15 @@ fun ChapterWebView(
consoleMessage?.let {
val message = it.message()
when {
message.startsWith("FootnoteDiag:") -> {
Timber.tag("FootnoteDiag")
.d("JS -> ${message.substringAfter("FootnoteDiag: ")}")
}
message.startsWith("TTS_CHAPTER_CHANGE_DIAG:") -> {
Timber.tag("TTS_CHAPTER_CHANGE_DIAG").d("JS -> ${message.substringAfter("TTS_CHAPTER_CHANGE_DIAG: ")}")
}
message.startsWith("BookmarkDiagnosis") -> {
Timber.tag("BookmarkDiagnosis")
.d("JS -> ${message.substringAfter("BookmarkDiagnosis: ")}")
@ -625,6 +647,12 @@ fun ChapterWebView(
AiJsBridge(ttsScope, onContentReadyForSummarization), "AiBridge"
)
addJavascriptInterface(
FootnoteJsBridge { html ->
this.post { onFootnoteRequested(html) }
}, "FootnoteBridge"
)
webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView?, request: WebResourceRequest?