From 02d5dfb37599ebd15fa34f5ad71056c70b319f40 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sun, 4 Jun 2023 16:50:09 +0300 Subject: [PATCH] Check if page file valid before page display --- .../koitharu/kotatsu/core/util/ext/File.kt | 2 ++ .../kotatsu/core/util/ext/Throwable.kt | 3 +++ .../koitharu/kotatsu/local/data/PagesCache.kt | 20 ++++++++++++++++++- .../kotatsu/reader/domain/PageLoader.kt | 13 +++++++++++- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt index ac98e2b48..79877887b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt @@ -26,6 +26,8 @@ fun File.takeIfReadable() = takeIf { it.exists() && it.canRead() } fun File.takeIfWriteable() = takeIf { it.exists() && it.canWrite() } +fun File.isNotEmpty() = length() != 0L + fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().use { it.readText() } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt index 3623d56d1..29defe131 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt @@ -87,3 +87,6 @@ fun Throwable.isWebViewUnavailable(): Boolean { return (this is AndroidRuntimeException && message?.contains("WebView") == true) || cause?.isWebViewUnavailable() == true } + +@Suppress("FunctionName") +fun NoSpaceLeftException() = IOException(MSG_NO_SPACE_LEFT) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/PagesCache.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/PagesCache.kt index 336fe6511..a39f01262 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/PagesCache.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/PagesCache.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.local.data import android.content.Context +import android.os.StatFs import com.tomclaw.cache.DiskLruCache import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers @@ -33,7 +34,8 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) { } private val lruCache = SuspendLazy { val dir = cacheDir.get() - val size = FileSize.MEGABYTES.convert(200, FileSize.BYTES) + val availableSize = getAvailableSizeMb() + val size = SIZE_DEFAULT.coerceIn(SIZE_MIN, availableSize) runCatchingCancellable { DiskLruCache.create(dir, size) }.recoverCatching { error -> @@ -63,4 +65,20 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) { file.delete() } } + + private suspend fun getAvailableSizeMb(): Long = runCatchingCancellable { + val statFs = StatFs(cacheDir.get().absolutePath) + FileSize.BYTES.convert(statFs.availableBytes, FileSize.MEGABYTES) + }.onFailure { + it.printStackTraceDebug() + }.getOrDefault(SIZE_DEFAULT) + + private companion object { + + val SIZE_MIN + get() = FileSize.MEGABYTES.convert(20, FileSize.BYTES) + + val SIZE_DEFAULT + get() = FileSize.MEGABYTES.convert(200, FileSize.BYTES) + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt index d37d80a30..eeac2ce29 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt @@ -12,6 +12,7 @@ import dagger.hilt.android.lifecycle.RetainedLifecycle import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ActivityRetainedScoped import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableStateFlow @@ -32,6 +33,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.FileSize import org.koitharu.kotatsu.core.util.RetainedLifecycleCoroutineScope import org.koitharu.kotatsu.core.util.ext.ensureSuccess +import org.koitharu.kotatsu.core.util.ext.isNotEmpty import org.koitharu.kotatsu.core.util.ext.ramAvailable import org.koitharu.kotatsu.core.util.ext.withProgress import org.koitharu.kotatsu.core.util.progress.ProgressDeferred @@ -103,7 +105,7 @@ class PageLoader @Inject constructor( } fun loadPageAsync(page: MangaPage, force: Boolean): ProgressDeferred { - var task = tasks[page.id] + var task = tasks[page.id]?.takeIf { it.isValid() } if (force) { task?.cancel() } else if (task?.isCancelled == false) { @@ -216,6 +218,15 @@ class PageLoader @Inject constructor( return context.ramAvailable <= FileSize.MEGABYTES.convert(PREFETCH_MIN_RAM_MB, FileSize.BYTES) } + private fun Deferred.isValid(): Boolean { + return if (isCompleted) { + val file = getCompleted() + file.exists() && file.isNotEmpty() + } else { + true + } + } + private class InternalErrorHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {