General improvments (#114)

* Add "Keep Screen On" feature to PDF and Epub readers.

* Add `rawLibraryFiles` to `MainViewModel` and improve file path resolution in `SharedComposables`.

Specifically:
- Updated `MainViewModel` state and UI logic to track and provide unfiltered library files.
- Enhanced file path formatting in `SharedComposables` to better handle Document, Tree, and primary storage URIs.
- Added scrollable support for long text values in `DetailItem` composable.
- Passed `rawLibraryFiles` to `FolderSyncScreen` to ensure sync logic uses the base file list.

* Update info bar background and text colors in EpubReaderScreen to match the selected theme.

* Prevent accidental UI bar toggles immediately after scrolling in EPUB vertical mode.

* feat: implement EPUB highlights sync and legacy migration

- Implement Cloud and Local Folder sync for EPUB highlights.
- Add highlight JSON serialization/deserialization helpers in EpubReaderAnnotations.
- Implement automatic migration of highlights from SharedPreferences to the Database on book open.
- Add cleanup logic to remove legacy SharedPreferences data after successful DB migration.
- Update RecentFilesRepository to trigger local folder metadata sync when highlights are modified.
- Update FolderSyncWorker to pull highlights and custom names from linked folder metadata.

* Update intent filters
This commit is contained in:
Aryan 2026-03-26 13:22:05 +05:30 committed by GitHub
parent 60dacf9c12
commit 4c5cd20b21
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 404 additions and 84 deletions

View file

@ -5,9 +5,7 @@ import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.util.Base64
import io.legere.pdfiumandroid.PdfiumCore
import io.legere.pdfiumandroid.suspend.PdfDocumentKt
import io.legere.pdfiumandroid.suspend.PdfiumCoreKt
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import timber.log.Timber
@ -219,9 +217,17 @@ object PdfToHtmlGenerator {
val c = rawText[i]
val code = c.code
if (code == 0 || code == 13) continue
if (code == 0) continue
if (c == '\r') {
commitLine()
continue
}
if (c == '\n') {
if (i > 0 && rawText[i - 1] == '\r') {
continue
}
commitLine()
continue
}
@ -292,7 +298,7 @@ object PdfToHtmlGenerator {
}
buildPageHtml(pageNumber, finalElements, headerFooterStrings)
} ?: buildEmptyPageSection(pageNumber)
}
} ?: buildEmptyPageSection(pageNumber)
} catch (e: Exception) {
Timber.tag(TAG).w(e, "Error extracting page $pageIdx")
@ -339,9 +345,12 @@ object PdfToHtmlGenerator {
var inParagraph = false
var inUl = false
var inOl = false
var inLi = false
fun closeParagraph() { if (inParagraph) { sb.append("</p>\n"); inParagraph = false } }
fun closeLi() { if (inLi) { sb.append("</li>\n"); inLi = false } }
fun closeList() {
closeLi()
if (inUl) { sb.append("</ul>\n"); inUl = false }
if (inOl) { sb.append("</ol>\n"); inOl = false }
}
@ -404,29 +413,40 @@ object PdfToHtmlGenerator {
sb.append("<$tag>${renderSpans(line.spans, insideHeading = true)}</$tag>\n")
}
isBullet -> {
closeParagraph()
closeParagraph(); closeLi()
if (inOl) { sb.append("</ol>\n"); inOl = false }
if (!inUl) { sb.append("<ul>\n"); inUl = true }
val content = trimmed.removePrefix("").removePrefix("").removePrefix("").removePrefix("").removePrefix("- ").trim()
sb.append("<li>${content.escapeHtml()}</li>\n")
sb.append("<li>${content.escapeHtml()}")
inLi = true
}
numberedMatch -> {
closeParagraph()
closeParagraph(); closeLi()
if (inUl) { sb.append("</ul>\n"); inUl = false }
if (!inOl) { sb.append("<ol>\n"); inOl = true }
val content = trimmed.substringAfter(" ").trim()
sb.append("<li>${content.escapeHtml()}</li>\n")
sb.append("<li>${content.escapeHtml()}")
inLi = true
}
shouldBreakParagraph -> {
closeList()
if (!inParagraph) { sb.append("<p>"); inParagraph = true }
sb.append(renderSpans(line.spans))
closeParagraph()
if (inLi) {
sb.append(" ").append(renderSpans(line.spans))
closeLi()
} else {
closeList()
if (!inParagraph) { sb.append("<p>"); inParagraph = true }
sb.append(renderSpans(line.spans))
closeParagraph()
}
}
else -> {
closeList()
if (!inParagraph) { sb.append("<p>"); inParagraph = true } else sb.append(" ")
sb.append(renderSpans(line.spans))
if (inLi) {
sb.append(" ").append(renderSpans(line.spans))
} else {
closeList()
if (!inParagraph) { sb.append("<p>"); inParagraph = true } else sb.append(" ")
sb.append(renderSpans(line.spans))
}
}
}
}

View file

@ -347,6 +347,17 @@ private const val PREF_EXTERNAL_DICT_PKG = "external_dictionary_package"
private const val PREF_EXTERNAL_TRANSLATE_PKG = "external_translate_package"
private const val PREF_EXTERNAL_SEARCH_PKG = "external_search_package"
private const val PDF_THEME_KEY = "pdf_reader_theme"
private const val PDF_KEEP_SCREEN_ON_KEY = "pdf_keep_screen_on_enabled"
private fun saveKeepScreenOn(context: Context, isEnabled: Boolean) {
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
prefs.edit { putBoolean(PDF_KEEP_SCREEN_ON_KEY, isEnabled) }
}
private fun loadKeepScreenOn(context: Context): Boolean {
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
return prefs.getBoolean(PDF_KEEP_SCREEN_ON_KEY, false)
}
private fun savePdfThemeId(context: Context, themeId: String) {
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
@ -1152,7 +1163,7 @@ fun PdfViewerScreen(
?: pdfUri.lastPathSegment ?: "Document.pdf"
}
}
val view = LocalView.current
var isDockDragging by remember { mutableStateOf(false) }
var initialScrollDone by remember { mutableStateOf(false) }
@ -1167,6 +1178,14 @@ fun PdfViewerScreen(
var isStylusOnlyMode by remember { mutableStateOf(loadStylusOnlyMode(context)) }
var currentTtsMode by remember { mutableStateOf(loadTtsMode(context)) }
var showTtsSettingsSheet by remember { mutableStateOf(false) }
var isKeepScreenOn by remember { mutableStateOf(loadKeepScreenOn(context)) }
DisposableEffect(isKeepScreenOn) {
view.keepScreenOn = isKeepScreenOn
onDispose {
view.keepScreenOn = false
}
}
var showDictionarySettingsSheet by remember { mutableStateOf(false) }
var useOnlineDictionary by remember { mutableStateOf(loadUseOnlineDict(context)) }
@ -1338,7 +1357,6 @@ fun PdfViewerScreen(
}
}
val view = LocalView.current
val window = (view.context as? Activity)?.window
LaunchedEffect(isFullScreen) {
if (window != null) {
@ -5423,6 +5441,23 @@ fun PdfViewerScreen(
}
})
HorizontalDivider()
DropdownMenuItem(
text = { Text("Keep Screen On") },
onClick = {
isKeepScreenOn = !isKeepScreenOn
saveKeepScreenOn(context, isKeepScreenOn)
showMoreMenu = false
},
trailingIcon = {
if (isKeepScreenOn) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = "Selected"
)
}
}
)
HorizontalDivider()
DropdownMenuItem(
text = { Text("Auto Scroll") },
enabled = !isTtsSessionActive && displayMode == DisplayMode.VERTICAL_SCROLL,