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:
parent
4cb25e6abd
commit
71e3614ad6
19 changed files with 1413 additions and 287 deletions
|
|
@ -498,6 +498,7 @@ fun EpubReaderHost(
|
|||
|
||||
var pendingNoteForNewHighlight by remember { mutableStateOf(false) }
|
||||
var highlightToNoteCfi by remember { mutableStateOf<String?>(null) }
|
||||
var activeFootnoteHtml by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
var showJustifyWarningDialog by remember { mutableStateOf(false) }
|
||||
var isNavigatingByToc by remember { mutableStateOf(false) }
|
||||
|
|
@ -509,6 +510,7 @@ fun EpubReaderHost(
|
|||
var systemUiMode by remember { mutableStateOf(loadSystemUiMode(context)) }
|
||||
var pageInfoMode by remember { mutableStateOf(loadPageInfoMode(context)) }
|
||||
var pullToTurnEnabled by remember { mutableStateOf(loadPullToTurn(context)) }
|
||||
var pullToTurnMultiplier by remember { mutableFloatStateOf(loadPullToTurnMultiplier(context)) }
|
||||
var showVisualOptionsSheet by remember { mutableStateOf(false) }
|
||||
var removeEdgePadding by remember { mutableStateOf(loadRemoveEdgePadding(context)) }
|
||||
|
||||
|
|
@ -809,7 +811,6 @@ fun EpubReaderHost(
|
|||
|
||||
var ttsShouldStartOnChapterLoad by remember { mutableStateOf(false) }
|
||||
var userStoppedTts by remember { mutableStateOf(false) }
|
||||
var skipChapterRequest by remember { mutableStateOf(false) }
|
||||
var ttsChapterIndex by remember { mutableStateOf<Int?>(null) }
|
||||
|
||||
var searchHighlightTarget by remember { mutableStateOf<SearchResult?>(null) }
|
||||
|
|
@ -878,7 +879,7 @@ fun EpubReaderHost(
|
|||
var activeFragmentId by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
val density = LocalDensity.current
|
||||
val dragThresholdPx = with(density) { DRAG_TO_CHANGE_CHAPTER_THRESHOLD_DP.toPx() }
|
||||
val dragThresholdPx = with(density) { DRAG_TO_CHANGE_CHAPTER_THRESHOLD_DP.toPx() * pullToTurnMultiplier }
|
||||
|
||||
var currentScrollYPosition by rememberSaveable(epubBook.title) {
|
||||
mutableIntStateOf(0)
|
||||
|
|
@ -1000,20 +1001,6 @@ fun EpubReaderHost(
|
|||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(skipChapterRequest) {
|
||||
if (skipChapterRequest) {
|
||||
skipChapterRequest = false
|
||||
if (ttsShouldStartOnChapterLoad && currentChapterIndex < chapters.size - 1) {
|
||||
Timber.d("Executing skip chapter request for continuous TTS.")
|
||||
currentScrollYPosition = 0
|
||||
currentScrollHeightValue = 0
|
||||
currentChapterIndex++
|
||||
} else {
|
||||
ttsShouldStartOnChapterLoad = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val searchState = rememberSearchState(scope = scope, searcher = epubSearcher)
|
||||
val speakerPlayer = remember(context, scope) {
|
||||
SpeakerSamplePlayer(context, scope, getAuthToken = { viewModel.getAuthToken() })
|
||||
|
|
@ -1135,7 +1122,7 @@ fun EpubReaderHost(
|
|||
}
|
||||
|
||||
fun startTts() {
|
||||
if (currentTtsMode == com.aryan.reader.tts.TtsPlaybackManager.TtsMode.CLOUD && credits <= 0) {
|
||||
if (currentTtsMode == TtsPlaybackManager.TtsMode.CLOUD && credits <= 0) {
|
||||
showInsufficientCreditsDialog = true
|
||||
return
|
||||
}
|
||||
|
|
@ -1193,7 +1180,7 @@ fun EpubReaderHost(
|
|||
)
|
||||
|
||||
fun startTtsFromSelectionPaginated(baseCfi: String, startOffset: Int) {
|
||||
if (currentTtsMode == com.aryan.reader.tts.TtsPlaybackManager.TtsMode.CLOUD && credits <= 0) {
|
||||
if (currentTtsMode == TtsPlaybackManager.TtsMode.CLOUD && credits <= 0) {
|
||||
showInsufficientCreditsDialog = true
|
||||
return
|
||||
}
|
||||
|
|
@ -1275,17 +1262,23 @@ fun EpubReaderHost(
|
|||
ttsChapterIndex = ttsChapterIndex,
|
||||
onTtsChapterIndexChange = { newIndex -> ttsChapterIndex = newIndex },
|
||||
onNavigateToChapter = { nextIndex ->
|
||||
Timber.tag("TTS_CHAPTER_CHANGE_DIAG").d("TtsSessionObserver triggered onNavigateToChapter to: $nextIndex")
|
||||
initialScrollTargetForChapter = ChapterScrollPosition.START
|
||||
cfiToLoad = null
|
||||
currentScrollYPosition = 0
|
||||
currentScrollHeightValue = 0
|
||||
currentChapterIndex = nextIndex
|
||||
},
|
||||
onToggleTtsStartOnLoad = { shouldStart -> ttsShouldStartOnChapterLoad = shouldStart },
|
||||
onToggleTtsStartOnLoad = { shouldStart ->
|
||||
Timber.tag("TTS_CHAPTER_CHANGE_DIAG").d("ttsShouldStartOnChapterLoad set to: $shouldStart")
|
||||
ttsShouldStartOnChapterLoad = shouldStart
|
||||
},
|
||||
userStoppedTts = userStoppedTts,
|
||||
scope = scope,
|
||||
currentTtsMode = currentTtsMode,
|
||||
getAuthToken = { viewModel.getAuthToken() }
|
||||
getAuthToken = { viewModel.getAuthToken() },
|
||||
locatorConverter = locatorConverter,
|
||||
epubBook = epubBook
|
||||
)
|
||||
|
||||
TtsHighlightHandler(
|
||||
|
|
@ -2411,6 +2404,8 @@ fun EpubReaderHost(
|
|||
CircularProgressIndicator()
|
||||
}
|
||||
} else if (chapterChunks.isNotEmpty()) {
|
||||
var hasRequestedExtractionForThisChapter by remember(targetChapterIndex) { mutableStateOf(false) }
|
||||
|
||||
val initialContentToLoad = remember(loadUpToChunkIndex, chapterChunks) {
|
||||
val targetIdx = loadUpToChunkIndex
|
||||
val startIdx = 0
|
||||
|
|
@ -2569,8 +2564,9 @@ fun EpubReaderHost(
|
|||
Timber.d("Auto-save enabled immediately.")
|
||||
}
|
||||
|
||||
if (ttsShouldStartOnChapterLoad) {
|
||||
if (ttsShouldStartOnChapterLoad && !hasRequestedExtractionForThisChapter) {
|
||||
Timber.d("Auto-starting TTS for new chapter ($targetChapterIndex).")
|
||||
hasRequestedExtractionForThisChapter = true
|
||||
scope.launch {
|
||||
delay(200)
|
||||
webViewRefForTts?.evaluateJavascript(
|
||||
|
|
@ -2773,7 +2769,7 @@ fun EpubReaderHost(
|
|||
onTtsTextReady = { jsonString ->
|
||||
scope.launch {
|
||||
val token = viewModel.getAuthToken()
|
||||
Timber.tag("TTS_LIST_DIAG").d("Vertical: Processing received JSON. Length: ${jsonString.length}") // Add this
|
||||
Timber.tag("TTS_LIST_DIAG").d("Vertical: Processing received JSON. Length: ${jsonString.length}")
|
||||
val ttsChunks = mutableListOf<TtsChunk>()
|
||||
try {
|
||||
val jsonArray = JSONArray(jsonString)
|
||||
|
|
@ -2808,18 +2804,21 @@ fun EpubReaderHost(
|
|||
Timber.d("Vertical: Final compiled TTS chunks size: ${ttsChunks.size}")
|
||||
|
||||
if (ttsChunks.isNotEmpty()) {
|
||||
if (currentTtsMode == com.aryan.reader.tts.TtsPlaybackManager.TtsMode.CLOUD && credits <= 0) {
|
||||
if (currentTtsMode == TtsPlaybackManager.TtsMode.CLOUD && credits <= 0) {
|
||||
showInsufficientCreditsDialog = true
|
||||
ttsShouldStartOnChapterLoad = false
|
||||
return@launch
|
||||
}
|
||||
|
||||
ttsShouldStartOnChapterLoad = false
|
||||
val chapterTitle = chapters.getOrNull(currentChapterIndex)?.title
|
||||
userStoppedTts = false
|
||||
|
||||
val chapterTitle = chapters.getOrNull(targetChapterIndex)?.title
|
||||
val coverUriString = coverImagePath?.let {
|
||||
Uri.fromFile(File(it)).toString()
|
||||
}
|
||||
ttsChapterIndex = currentChapterIndex
|
||||
ttsChapterIndex = targetChapterIndex
|
||||
|
||||
ttsController.start(
|
||||
chunks = ttsChunks,
|
||||
bookTitle = epubBook.title,
|
||||
|
|
@ -2830,12 +2829,16 @@ fun EpubReaderHost(
|
|||
authToken = token
|
||||
)
|
||||
} else {
|
||||
Timber.w("No TTS chunks were created from JSON, not starting TTS."
|
||||
)
|
||||
Timber.w("No TTS chunks were created from JSON, not starting TTS.")
|
||||
if (ttsShouldStartOnChapterLoad) {
|
||||
Timber.d("Empty chapter detected during continuous TTS. Requesting skip."
|
||||
)
|
||||
skipChapterRequest = true
|
||||
Timber.d("Empty chapter detected during start. Advancing UI to next chapter.")
|
||||
val nextIdx = targetChapterIndex + 1
|
||||
if (nextIdx < chapters.size) {
|
||||
initialScrollTargetForChapter = ChapterScrollPosition.START
|
||||
currentScrollYPosition = 0
|
||||
currentScrollHeightValue = 0
|
||||
currentChapterIndex = nextIdx
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2899,6 +2902,9 @@ fun EpubReaderHost(
|
|||
)
|
||||
}
|
||||
},
|
||||
onFootnoteRequested = { html ->
|
||||
activeFootnoteHtml = html
|
||||
},
|
||||
isProUser = isProUser,
|
||||
isOss = BuildConfig.FLAVOR == "oss",
|
||||
onShowDictionaryUpsellDialog = {
|
||||
|
|
@ -3241,6 +3247,9 @@ fun EpubReaderHost(
|
|||
pendingNoteForNewHighlight = true
|
||||
}
|
||||
},
|
||||
onFootnoteRequested = { html ->
|
||||
activeFootnoteHtml = html
|
||||
},
|
||||
onHighlightDeleted = { cfi ->
|
||||
val toRemove = userHighlights.find { it.cfi == cfi }
|
||||
if (toRemove != null) {
|
||||
|
|
@ -4317,6 +4326,15 @@ fun EpubReaderHost(
|
|||
}
|
||||
}
|
||||
|
||||
if (activeFootnoteHtml != null) {
|
||||
FootnoteBottomSheet(
|
||||
htmlContent = activeFootnoteHtml!!,
|
||||
effectiveBg = effectiveBg,
|
||||
effectiveText = effectiveText,
|
||||
onDismiss = { activeFootnoteHtml = null }
|
||||
)
|
||||
}
|
||||
|
||||
CustomTopBanner(bannerMessage = bannerMessage)
|
||||
}
|
||||
}
|
||||
|
|
@ -4452,6 +4470,11 @@ fun EpubReaderHost(
|
|||
removeEdgePadding = it
|
||||
saveRemoveEdgePadding(context, it)
|
||||
},
|
||||
pullToTurnMultiplier = pullToTurnMultiplier,
|
||||
onPullToTurnMultiplierChange = {
|
||||
pullToTurnMultiplier = it
|
||||
savePullToTurnMultiplier(context, it)
|
||||
},
|
||||
onDismiss = { showVisualOptionsSheet = false }
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue