Bug and crash fixes (#132)
* feat(EPUB): adapt status bar icon visibility to reader theme
Pass the reader's active isDarkTheme state to EpubReaderSystemUiController to ensure status bar icons automatically switch between light and dark to remain visible against the selected background/page color.
* Implement storage tracking and optimize cache management
This commit introduces a `StorageTracker` to monitor disk usage and implements several optimizations to prevent cache bloat and handle orphaned files.
* refactor: unify book extraction paths and fix cache leaks
- Replaced random UUID and title-based hashing with stable `bookId` for extraction directories across Epub, Mobi, and FB2 parsers.
- Standardized all temporary extraction paths to `cache/imported_file_${bookId}`.
- Updated MainViewModel and MetadataExtractionWorker to provide the mandatory `bookId` during parsing.
- Ensured `clearImportedFileCache` successfully targets the correct directories on book deletion.
- Added a legacy cleanup task in `sweepOrphanedCache` to reclaim storage from old `extracted_epubs` folders.
* fix(pdf): handle NoClassDefFoundError for PdfPasswordException
Updated the PDF loading logic in PdfViewerScreen to catch Throwable
instead of Exception. This prevents the app from crashing when the
underlying pdfium library attempts to throw a PdfPasswordException
that is missing from the runtime classpath (NoClassDefFoundError).
* fix(tts): set language before voice to prevent variant reset
* EpubReaderScreen: increase scroll-hide ignore duration for tap toggles
Increase the threshold for ignoring tap toggles after bars are hidden by scrolling from 250ms to 400ms. This prevents accidental UI toggles caused by "sloppy taps" immediately following a scroll action.
* fix: resolve large bitmap crash on tall PDF pages
- Caps base layer bitmap dimensions to 3000px to prevent GPU texture limit crashes.
- Enables tiled rendering for large pages even at 1x zoom to maintain sharpness.
- Optimizes PdfBitmapPool to support rectangular bitmap allocations.
* Common.kt: ensure unique keys in voice list
* fix: handle ActivityNotFoundException in folder sync screen
* Improve file deletion logic and fix chapter index bounds in epub reader
* fix: ensure WebView bridge callbacks run on UI thread
This resolves a WebViewMethodCalledOnWrongThreadViolation in vertical mode that was preventing chapter chunks from loading beyond the initial viewport.
This commit is contained in:
parent
0d37bcefc3
commit
355664fbcc
16 changed files with 502 additions and 280 deletions
|
|
@ -125,7 +125,12 @@ class MobiParser(private val context: Context) {
|
|||
return File(parentDir, bookIdentifier)
|
||||
}
|
||||
|
||||
suspend fun createMobiBook(inputStream: InputStream, originalBookNameHint: String): EpubBook? = withContext(Dispatchers.IO) {
|
||||
suspend fun createMobiBook(
|
||||
inputStream: InputStream,
|
||||
bookId: String,
|
||||
originalBookNameHint: String,
|
||||
parseContent: Boolean = true
|
||||
): EpubBook? = withContext(Dispatchers.IO) {
|
||||
val tempFile = File.createTempFile("temp_mobi_", ".mobi", context.cacheDir)
|
||||
try {
|
||||
tempFile.outputStream().use { output ->
|
||||
|
|
@ -157,25 +162,25 @@ class MobiParser(private val context: Context) {
|
|||
val bookTitle = parsedData.title ?: originalBookNameHint
|
||||
val bookAuthor = parsedData.author ?: "Unknown Author"
|
||||
|
||||
val bookIdentifier = bookTitle.asFileName() + "_" + UUID.randomUUID().toString().substring(0, 8)
|
||||
val extractionDir = getBookExtractionDir(bookIdentifier)
|
||||
val extractionDir = File(context.cacheDir, "imported_file_$bookId")
|
||||
extractionDir.mkdirs()
|
||||
|
||||
// This map is the key. It maps the 1-based sequential index of an image to its new path.
|
||||
val sequentialImageMap = parsedData.resources
|
||||
.filter { it.mediaType.startsWith("image/") }
|
||||
.sortedBy { it.uid } // Sort by UID to ensure order is correct
|
||||
.sortedBy { it.uid }
|
||||
.mapIndexed { index, resource -> (index + 1) to resource.path }
|
||||
.toMap()
|
||||
|
||||
parsedData.resources.forEach { resource ->
|
||||
try {
|
||||
val file = File(extractionDir, resource.path)
|
||||
file.parentFile?.mkdirs()
|
||||
file.writeBytes(resource.data)
|
||||
Timber.d("Wrote resource to disk -> Path: ${file.absolutePath}, Type: ${resource.mediaType}, Size: ${resource.data.size}")
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Parser: FAILED to write resource to disk: ${resource.path}")
|
||||
if (parseContent) {
|
||||
parsedData.resources.forEach { resource ->
|
||||
try {
|
||||
val file = File(extractionDir, resource.path)
|
||||
file.parentFile?.mkdirs()
|
||||
file.writeBytes(resource.data)
|
||||
Timber.d("Wrote resource to disk -> Path: ${file.absolutePath}")
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Parser: FAILED to write resource to disk: ${resource.path}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,26 +255,28 @@ class MobiParser(private val context: Context) {
|
|||
|
||||
Timber.d("Successfully split content into ${chapterHtmlParts.size} chapters.")
|
||||
|
||||
val epubChapters = chapterHtmlParts.mapIndexedNotNull { index, (chapterHtml, title) ->
|
||||
try {
|
||||
val rewrittenHtml = processChapterHtml(chapterHtml)
|
||||
val doc = Jsoup.parse(rewrittenHtml)
|
||||
val chapterFileName = "chapter_$index.html"
|
||||
val chapterFile = File(extractionDir, chapterFileName)
|
||||
chapterFile.writeText(rewrittenHtml)
|
||||
EpubChapter(
|
||||
chapterId = "mobi_chapter_$index",
|
||||
title = title,
|
||||
absPath = chapterFileName,
|
||||
htmlFilePath = chapterFileName,
|
||||
htmlContent = rewrittenHtml,
|
||||
plainTextContent = doc.text()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to process split chapter $index")
|
||||
null
|
||||
val epubChapters = if (parseContent) {
|
||||
chapterHtmlParts.mapIndexedNotNull { index, (chapterHtml, title) ->
|
||||
try {
|
||||
val rewrittenHtml = processChapterHtml(chapterHtml)
|
||||
val doc = Jsoup.parse(rewrittenHtml)
|
||||
val chapterFileName = "chapter_$index.html"
|
||||
val chapterFile = File(extractionDir, chapterFileName)
|
||||
chapterFile.writeText(rewrittenHtml)
|
||||
EpubChapter(
|
||||
chapterId = "mobi_chapter_$index",
|
||||
title = title,
|
||||
absPath = chapterFileName,
|
||||
htmlFilePath = chapterFileName,
|
||||
htmlContent = rewrittenHtml,
|
||||
plainTextContent = doc.text()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to process split chapter $index")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
} else emptyList()
|
||||
|
||||
val images = parsedData.resources
|
||||
.filter { it.mediaType.startsWith("image/") }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue