mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
refactor(cover): Adjust cover (memory) cache key
Hopefully fix library image flickering on resume/bind
This commit is contained in:
parent
df66327996
commit
653b2d7839
10 changed files with 62 additions and 48 deletions
|
@ -53,6 +53,7 @@ import eu.kanade.tachiyomi.util.system.AuthenticatorUtil
|
|||
import eu.kanade.tachiyomi.util.system.launchIO
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import eu.kanade.tachiyomi.util.system.notification
|
||||
import java.security.Security
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
@ -69,7 +70,6 @@ import yokai.core.migration.migrations.migrations
|
|||
import yokai.domain.base.BasePreferences
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import java.security.Security
|
||||
|
||||
open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory {
|
||||
|
||||
|
@ -251,7 +251,7 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
|
|||
}
|
||||
|
||||
diskCache(diskCacheLazy::value)
|
||||
// memoryCache { MemoryCache.Builder().maxSizePercent(this@App, 0.40).build() }
|
||||
//memoryCache { MemoryCache.Builder().maxSizePercent(this@App, 0.40).build() }
|
||||
crossfade(true)
|
||||
allowRgb565(this@App.getSystemService<ActivityManager>()!!.isLowRamDevice)
|
||||
allowHardware(true)
|
||||
|
|
|
@ -6,7 +6,6 @@ import co.touchlab.kermit.Logger
|
|||
import coil3.imageLoader
|
||||
import coil3.memory.MemoryCache
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaImpl
|
||||
import eu.kanade.tachiyomi.domain.manga.models.Manga
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.system.e
|
||||
|
@ -14,16 +13,16 @@ import eu.kanade.tachiyomi.util.system.executeOnIO
|
|||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.system.withIOContext
|
||||
import eu.kanade.tachiyomi.util.system.withUIContext
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.*
|
||||
|
||||
/**
|
||||
* Class used to create cover cache.
|
||||
|
@ -171,7 +170,7 @@ class CoverCache(val context: Context) {
|
|||
fun setCustomCoverToCache(manga: Manga, inputStream: InputStream) {
|
||||
getCustomCoverFile(manga).outputStream().use {
|
||||
inputStream.copyTo(it)
|
||||
context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
|
||||
removeFromMemory(manga, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,7 +184,7 @@ class CoverCache(val context: Context) {
|
|||
val result = getCustomCoverFile(manga).let {
|
||||
it.exists() && it.delete()
|
||||
}
|
||||
context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
|
||||
removeFromMemory(manga, true)
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -195,18 +194,28 @@ class CoverCache(val context: Context) {
|
|||
* @param thumbnailUrl the thumbnail url.
|
||||
* @return cover image.
|
||||
*/
|
||||
fun getCoverFile(manga: Manga): File {
|
||||
val hashKey = DiskUtil.hashKeyForDisk((manga.thumbnail_url.orEmpty()))
|
||||
return if (manga.favorite) {
|
||||
File(cacheDir, hashKey)
|
||||
} else {
|
||||
File(onlineCoverDirectory, hashKey)
|
||||
fun getCoverFile(mangaThumbnailUrl: String?, isOnline: Boolean = false): File? {
|
||||
return mangaThumbnailUrl?.let {
|
||||
File(if (!isOnline) cacheDir else onlineCoverDirectory, DiskUtil.hashKeyForDisk(it))
|
||||
}
|
||||
}
|
||||
|
||||
fun removeFromMemory(manga: Manga, custom: Boolean = false) {
|
||||
if (custom) {
|
||||
context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
|
||||
return
|
||||
}
|
||||
|
||||
manga.thumbnail_url?.let {
|
||||
if (it.isEmpty()) return
|
||||
context.imageLoader.memoryCache
|
||||
?.remove(MemoryCache.Key(if (!manga.favorite) it else DiskUtil.hashKeyForDisk(it)))
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteFromCache(name: String?) {
|
||||
if (name.isNullOrEmpty()) return
|
||||
val file = getCoverFile(MangaImpl().apply { thumbnail_url = name })
|
||||
val file = getCoverFile(name, true) ?: return
|
||||
context.imageLoader.memoryCache?.remove(MemoryCache.Key(file.name))
|
||||
if (file.exists()) file.delete()
|
||||
}
|
||||
|
@ -225,12 +234,11 @@ class CoverCache(val context: Context) {
|
|||
if (manga.thumbnail_url.isNullOrEmpty()) return
|
||||
|
||||
// Remove file
|
||||
val file = getCoverFile(manga)
|
||||
if (deleteCustom) deleteCustomCover(manga)
|
||||
if (file.exists()) {
|
||||
context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
|
||||
file.delete()
|
||||
getCoverFile(manga.thumbnail_url, !manga.favorite)?.let {
|
||||
removeFromMemory(manga)
|
||||
it.delete()
|
||||
}
|
||||
if (deleteCustom) deleteCustomCover(manga)
|
||||
}
|
||||
|
||||
private fun getCacheDir(dir: String): File {
|
||||
|
|
|
@ -6,7 +6,6 @@ import androidx.palette.graphics.Palette
|
|||
import coil3.Image
|
||||
import coil3.ImageLoader
|
||||
import coil3.imageLoader
|
||||
import coil3.memory.MemoryCache
|
||||
import coil3.request.Disposable
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.target.ImageViewTarget
|
||||
|
@ -26,15 +25,15 @@ class LibraryMangaImageTarget(
|
|||
super.onError(error)
|
||||
if (manga.favorite) {
|
||||
launchIO {
|
||||
val file = coverCache.getCoverFile(manga)
|
||||
val file = coverCache.getCoverFile(manga.thumbnail_url, false)
|
||||
// if the file exists and the there was still an error then the file is corrupted
|
||||
if (file.exists()) {
|
||||
if (file != null && file.exists()) {
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeFile(file.path, options)
|
||||
if (options.outWidth == -1 || options.outHeight == -1) {
|
||||
coverCache.removeFromMemory(manga)
|
||||
file.delete()
|
||||
view.context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +51,6 @@ inline fun ImageView.loadManga(
|
|||
.data(manga)
|
||||
.target(LibraryMangaImageTarget(this, manga))
|
||||
.apply(builder)
|
||||
.memoryCacheKey(manga.key())
|
||||
.build()
|
||||
return imageLoader.enqueue(request)
|
||||
}
|
||||
|
|
|
@ -87,8 +87,8 @@ class MangaCoverFetcher(
|
|||
val customCoverLoader = tryCustomCover()
|
||||
if (customCoverLoader != null) return customCoverLoader
|
||||
}
|
||||
val coverFile = coverCache.getCoverFile(manga)
|
||||
if (!shouldFetchRemotely && coverFile.exists() && options.diskCachePolicy.readEnabled) {
|
||||
val coverFile = coverCache.getCoverFile(manga.thumbnail_url, !manga.favorite)
|
||||
if (!shouldFetchRemotely && coverFile != null && coverFile.exists() && options.diskCachePolicy.readEnabled) {
|
||||
if (!manga.favorite) {
|
||||
coverFile.setLastModified(Date().time)
|
||||
}
|
||||
|
|
|
@ -2,12 +2,16 @@ package eu.kanade.tachiyomi.data.coil
|
|||
|
||||
import coil3.key.Keyer
|
||||
import coil3.request.Options
|
||||
import eu.kanade.tachiyomi.data.database.models.hasCustomCover
|
||||
import eu.kanade.tachiyomi.domain.manga.models.Manga
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
|
||||
class MangaCoverKeyer : Keyer<Manga> {
|
||||
override fun key(data: Manga, options: Options): String? {
|
||||
if (data.thumbnail_url.isNullOrBlank()) return null
|
||||
val hasCustomCover by lazy { data.hasCustomCover() }
|
||||
if (data.thumbnail_url.isNullOrBlank() && !hasCustomCover) return null
|
||||
if (hasCustomCover) return data.key()
|
||||
|
||||
return if (!data.favorite) {
|
||||
data.thumbnail_url!!
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package eu.kanade.tachiyomi.data.database.models
|
||||
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.domain.manga.models.Manga
|
||||
import eu.kanade.tachiyomi.domain.manga.models.Manga.Companion.TYPE_COMIC
|
||||
|
@ -14,6 +15,7 @@ import eu.kanade.tachiyomi.ui.reader.settings.OrientationType
|
|||
import eu.kanade.tachiyomi.ui.reader.settings.ReadingModeType
|
||||
import eu.kanade.tachiyomi.util.manga.MangaCoverMetadata
|
||||
import eu.kanade.tachiyomi.util.system.withIOContext
|
||||
import java.util.Locale
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
@ -21,7 +23,6 @@ import yokai.data.updateStrategyAdapter
|
|||
import yokai.domain.chapter.interactor.GetChapter
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import java.util.*
|
||||
|
||||
fun Manga.sortDescending(preferences: PreferencesHelper): Boolean =
|
||||
if (usesLocalSort) sortDescending else preferences.chaptersDescAsDefault().get()
|
||||
|
@ -221,3 +222,7 @@ fun Manga.Companion.mapper(
|
|||
this.filtered_scanlators = filteredScanlators
|
||||
this.update_strategy = updateStrategy.let(updateStrategyAdapter::decode)
|
||||
}
|
||||
|
||||
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
||||
return coverCache.getCustomCoverFile(this).exists()
|
||||
}
|
||||
|
|
|
@ -1646,8 +1646,8 @@ class LibraryPresenter(
|
|||
libraryManga.forEach { manga ->
|
||||
if (manga.id == null) return@forEach
|
||||
if (manga.thumbnail_url?.startsWith("custom", ignoreCase = true) == true) {
|
||||
val file = cc.getCoverFile(manga)
|
||||
if (file.exists()) {
|
||||
val file = cc.getCoverFile(manga.thumbnail_url, !manga.favorite)
|
||||
if (file != null && file.exists()) {
|
||||
file.renameTo(cc.getCustomCoverFile(manga))
|
||||
}
|
||||
manga.thumbnail_url =
|
||||
|
|
|
@ -136,6 +136,11 @@ import eu.kanade.tachiyomi.util.view.snack
|
|||
import eu.kanade.tachiyomi.util.view.toolbarHeight
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.widget.LinearLayoutManagerAccurateOffset
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import uy.kohesive.injekt.Injekt
|
||||
|
@ -143,11 +148,6 @@ import uy.kohesive.injekt.api.get
|
|||
import yokai.i18n.MR
|
||||
import yokai.presentation.core.Constants
|
||||
import yokai.util.lang.getString
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
import android.R as AR
|
||||
|
||||
class MangaDetailsController :
|
||||
|
@ -580,7 +580,6 @@ class MangaDetailsController :
|
|||
val view = view ?: return
|
||||
|
||||
val request = ImageRequest.Builder(view.context).data(presenter.manga).allowHardware(false)
|
||||
.memoryCacheKey(presenter.manga.key())
|
||||
.target(
|
||||
onSuccess = { image ->
|
||||
val drawable = image.asDrawable(view.context.resources)
|
||||
|
@ -619,8 +618,8 @@ class MangaDetailsController :
|
|||
getHeader()?.updateCover(manga!!)
|
||||
},
|
||||
onError = {
|
||||
val file = presenter.coverCache.getCoverFile(manga!!)
|
||||
if (file.exists()) {
|
||||
val file = presenter.coverCache.getCoverFile(manga!!.thumbnail_url, !manga!!.favorite)
|
||||
if (file != null && file.exists()) {
|
||||
file.delete()
|
||||
setPaletteColor()
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.graphics.Bitmap
|
|||
import android.net.Uri
|
||||
import androidx.core.net.toFile
|
||||
import coil3.imageLoader
|
||||
import coil3.memory.MemoryCache
|
||||
import coil3.request.CachePolicy
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.request.SuccessResult
|
||||
|
@ -21,6 +20,7 @@ import eu.kanade.tachiyomi.data.database.models.Track
|
|||
import eu.kanade.tachiyomi.data.database.models.bookmarkedFilter
|
||||
import eu.kanade.tachiyomi.data.database.models.chapterOrder
|
||||
import eu.kanade.tachiyomi.data.database.models.downloadedFilter
|
||||
import eu.kanade.tachiyomi.data.database.models.hasCustomCover
|
||||
import eu.kanade.tachiyomi.data.database.models.readFilter
|
||||
import eu.kanade.tachiyomi.data.database.models.sortDescending
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
|
@ -403,7 +403,7 @@ class MangaDetailsPresenter(
|
|||
.build()
|
||||
|
||||
if (preferences.context.imageLoader.execute(request) is SuccessResult) {
|
||||
preferences.context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
|
||||
coverCache.removeFromMemory(manga, manga.hasCustomCover(coverCache))
|
||||
withContext(Dispatchers.Main) {
|
||||
view?.setPaletteColor()
|
||||
}
|
||||
|
@ -920,8 +920,8 @@ class MangaDetailsPresenter(
|
|||
}
|
||||
|
||||
private fun saveCover(directory: UniFile): UniFile {
|
||||
val cover = coverCache.getCustomCoverFile(manga).takeIf { it.exists() } ?: coverCache.getCoverFile(manga)
|
||||
val type = ImageUtil.findImageType(cover.inputStream())
|
||||
val cover = coverCache.getCustomCoverFile(manga).takeIf { it.exists() } ?: coverCache.getCoverFile(manga.thumbnail_url, !manga.favorite)
|
||||
val type = cover?.let { ImageUtil.findImageType(it.inputStream()) }
|
||||
?: throw Exception("Not an image")
|
||||
|
||||
// Build destination file.
|
||||
|
|
|
@ -8,9 +8,9 @@ import eu.kanade.tachiyomi.data.coil.getBestColor
|
|||
import eu.kanade.tachiyomi.data.database.models.dominantCoverColors
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.domain.manga.models.Manga
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
import java.util.concurrent.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
/** Object that holds info about a covers size ratio + dominant colors */
|
||||
object MangaCoverMetadata {
|
||||
|
@ -54,9 +54,9 @@ object MangaCoverMetadata {
|
|||
remove(manga)
|
||||
}
|
||||
if (manga.vibrantCoverColor != null && !manga.favorite) return
|
||||
val file = ogFile ?: coverCache.getCustomCoverFile(manga).takeIf { it.exists() } ?: coverCache.getCoverFile(manga)
|
||||
val file = ogFile ?: coverCache.getCustomCoverFile(manga).takeIf { it.exists() } ?: coverCache.getCoverFile(manga.thumbnail_url, !manga.favorite)
|
||||
// if the file exists and the there was still an error then the file is corrupted
|
||||
if (file.exists()) {
|
||||
if (file != null && file.exists()) {
|
||||
val options = BitmapFactory.Options()
|
||||
val hasVibrantColor = if (manga.favorite) manga.vibrantCoverColor != null else true
|
||||
if (manga.dominantCoverColors != null && hasVibrantColor && !force) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue