From b1187c611a37895ef1d804c6f6e0bfd132313948 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Fri, 2 Jun 2023 15:29:55 +0300 Subject: [PATCH] Check available ram before pages prefetch --- .../org/koitharu/kotatsu/core/util/ext/Android.kt | 10 ++++++++-- .../org/koitharu/kotatsu/local/data/PagesCache.kt | 3 ++- .../koitharu/kotatsu/reader/domain/PageLoader.kt | 15 ++++++++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt index 9f5941449..393a2095b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Android.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.util.ext import android.app.Activity import android.app.ActivityManager +import android.app.ActivityManager.MemoryInfo import android.app.ActivityOptions import android.content.Context import android.content.Context.ACTIVITY_SERVICE @@ -23,8 +24,6 @@ import android.widget.Toast import androidx.activity.result.ActivityResultLauncher import androidx.annotation.IntegerRes import androidx.core.app.ActivityOptionsCompat -import androidx.core.content.IntentCompat -import androidx.core.content.PackageManagerCompat import androidx.core.os.LocaleListCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.coroutineScope @@ -142,6 +141,13 @@ fun Context.isLowRamDevice(): Boolean { return activityManager?.isLowRamDevice ?: false } +val Context.ramAvailable: Long + get() { + val result = MemoryInfo() + activityManager?.getMemoryInfo(result) + return result.availMem + } + fun scaleUpActivityOptionsOf(view: View): ActivityOptions = ActivityOptions.makeScaleUpAnimation( view, 0, 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 755ed222b..336fe6511 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 @@ -54,9 +54,10 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) { suspend fun put(url: String, source: Source): File = withContext(Dispatchers.IO) { val file = File(cacheDir.get().parentFile, url.longHashCode().toString()) try { - file.sink(append = false).buffer().use { + val bytes = file.sink(append = false).buffer().use { it.writeAllCancellable(source) } + check(bytes != 0L) { "No data has been written" } lruCache.get().put(url, file) } finally { file.delete() 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 19bf8bda9..d37d80a30 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 @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.reader.domain +import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri @@ -8,6 +9,7 @@ import androidx.collection.LongSparseArray import androidx.collection.set import dagger.hilt.android.ActivityRetainedLifecycle 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.Dispatchers @@ -27,8 +29,10 @@ import org.koitharu.kotatsu.core.network.MangaHttpClient import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository 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.ramAvailable import org.koitharu.kotatsu.core.util.ext.withProgress import org.koitharu.kotatsu.core.util.progress.ProgressDeferred import org.koitharu.kotatsu.local.data.CbzFilter @@ -47,6 +51,7 @@ import kotlin.coroutines.CoroutineContext @ActivityRetainedScoped class PageLoader @Inject constructor( + @ApplicationContext private val context: Context, lifecycle: ActivityRetainedLifecycle, @MangaHttpClient private val okHttp: OkHttpClient, private val cache: PagesCache, @@ -76,7 +81,7 @@ class PageLoader @Inject constructor( } fun isPrefetchApplicable(): Boolean { - return repository is RemoteMangaRepository && settings.isPagesPreloadEnabled + return repository is RemoteMangaRepository && settings.isPagesPreloadEnabled && !isLowRam() } @AnyThread @@ -117,6 +122,9 @@ class PageLoader @Inject constructor( suspend fun convertInPlace(file: File) { convertLock.withLock { + if (context.ramAvailable < file.length() * 2) { + return@withLock + } runInterruptible(Dispatchers.Default) { val image = BitmapFactory.decodeFile(file.absolutePath) try { @@ -204,6 +212,10 @@ class PageLoader @Inject constructor( } } + private fun isLowRam(): Boolean { + return context.ramAvailable <= FileSize.MEGABYTES.convert(PREFETCH_MIN_RAM_MB, FileSize.BYTES) + } + private class InternalErrorHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler { @@ -216,6 +228,7 @@ class PageLoader @Inject constructor( private const val PROGRESS_UNDEFINED = -1f private const val PREFETCH_LIMIT_DEFAULT = 10 + private const val PREFETCH_MIN_RAM_MB = 80L fun createPageRequest(page: MangaPage, pageUrl: String) = Request.Builder() .url(pageUrl)