diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 6a273dc309..2c487bf5b9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -39,6 +39,7 @@ import eu.kanade.tachiyomi.core.preference.PreferenceStore import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer +import eu.kanade.tachiyomi.data.coil.MangaKeyer import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.preference.PreferencesHelper @@ -243,8 +244,10 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F add(TachiyomiImageDecoder.Factory()) // Fetcher.Factory add(BufferedSourceFetcher.Factory()) - add(MangaCoverFetcher.Factory(callFactoryLazy)) + add(MangaCoverFetcher.MangaFactory(callFactoryLazy)) + add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy)) // Keyer + add(MangaKeyer()) add(MangaCoverKeyer()) } crossfade(true) diff --git a/app/src/main/java/eu/kanade/tachiyomi/appwidget/UpdatesGridGlanceWidget.kt b/app/src/main/java/eu/kanade/tachiyomi/appwidget/UpdatesGridGlanceWidget.kt index e240334628..0cd9f75e50 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/appwidget/UpdatesGridGlanceWidget.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/appwidget/UpdatesGridGlanceWidget.kt @@ -37,10 +37,12 @@ import eu.kanade.tachiyomi.domain.manga.models.Manga import eu.kanade.tachiyomi.ui.recents.RecentsPresenter import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.launchIO +import java.util.Calendar +import java.util.Date +import kotlin.math.min import kotlinx.coroutines.MainScope import uy.kohesive.injekt.injectLazy -import java.util.* -import kotlin.math.min +import yokai.domain.manga.models.cover class UpdatesGridGlanceWidget : GlanceAppWidget() { private val app: Application by injectLazy() @@ -97,7 +99,7 @@ class UpdatesGridGlanceWidget : GlanceAppWidget() { .map { it.first } .map { updatesView -> val request = ImageRequest.Builder(app) - .data(updatesView) + .data(updatesView.cover()) .memoryCachePolicy(CachePolicy.DISABLED) .precision(Precision.EXACT) .size(widthPx, heightPx) diff --git a/app/src/main/java/eu/kanade/tachiyomi/appwidget/components/UpdatesMangaCover.kt b/app/src/main/java/eu/kanade/tachiyomi/appwidget/components/UpdatesMangaCover.kt index 6856ddb787..a9aa232d08 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/appwidget/components/UpdatesMangaCover.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/appwidget/components/UpdatesMangaCover.kt @@ -11,8 +11,6 @@ import androidx.glance.layout.ContentScale import androidx.glance.layout.fillMaxSize import androidx.glance.layout.size import eu.kanade.tachiyomi.R -import yokai.i18n.MR -import yokai.util.lang.getString import eu.kanade.tachiyomi.appwidget.util.appWidgetInnerRadius val CoverWidth = 58.dp diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt index 54d7d1643b..286ed77e21 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt @@ -158,8 +158,10 @@ class CoverCache(val context: Context) { * @param manga the manga. * @return cover image. */ - fun getCustomCoverFile(manga: Manga): File { - return File(customCoverCacheDir, DiskUtil.hashKeyForDisk(manga.id.toString())) + fun getCustomCoverFile(manga: Manga): File = getCustomCoverFile(manga.id) + + fun getCustomCoverFile(mangaId: Long?): File { + return File(customCoverCacheDir, DiskUtil.hashKeyForDisk(mangaId.toString())) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/LibraryMangaImageTarget.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/LibraryMangaImageTarget.kt index bae6a8c78f..3de5226576 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/LibraryMangaImageTarget.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/LibraryMangaImageTarget.kt @@ -11,29 +11,29 @@ import coil3.request.ImageRequest import coil3.target.ImageViewTarget import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.models.updateCoverLastModified -import eu.kanade.tachiyomi.domain.manga.models.Manga import eu.kanade.tachiyomi.util.system.launchIO import uy.kohesive.injekt.injectLazy +import yokai.domain.manga.models.MangaCover class LibraryMangaImageTarget( override val view: ImageView, - val manga: Manga, + val cover: MangaCover, ) : ImageViewTarget(view) { private val coverCache: CoverCache by injectLazy() override fun onError(error: Image?) { super.onError(error) - if (manga.favorite) { + if (cover.inLibrary) { launchIO { - val file = coverCache.getCoverFile(manga.thumbnail_url, false) + val file = coverCache.getCoverFile(cover.url, false) // if the file exists and the there was still an error then the file is corrupted if (file != null && file.exists()) { val options = BitmapFactory.Options() options.inJustDecodeBounds = true BitmapFactory.decodeFile(file.path, options) if (options.outWidth == -1 || options.outHeight == -1) { - manga.updateCoverLastModified() + cover.updateCoverLastModified() file.delete() } } @@ -44,13 +44,13 @@ class LibraryMangaImageTarget( @JvmSynthetic inline fun ImageView.loadManga( - manga: Manga, + cover: MangaCover, imageLoader: ImageLoader = context.imageLoader, builder: ImageRequest.Builder.() -> Unit = {}, ): Disposable { val request = ImageRequest.Builder(context) - .data(manga) - .target(LibraryMangaImageTarget(this, manga)) + .data(cover) + .target(LibraryMangaImageTarget(this, cover)) .apply(builder) .build() return imageLoader.enqueue(request) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt index b270c1410a..c8b8d0d0b9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt @@ -39,9 +39,12 @@ import okio.buffer import okio.sink import okio.source import uy.kohesive.injekt.injectLazy +import yokai.domain.manga.models.MangaCover class MangaCoverFetcher( - private val manga: Manga, + private val mangaId: Long?, + private val url: String?, + private val isInLibrary: Boolean, private val sourceLazy: Lazy, private val options: Options, private val callFactoryLazy: Lazy, @@ -53,7 +56,6 @@ class MangaCoverFetcher( private val diskCacheKey: String get() = diskCacheKeyLazy.value - private lateinit var url: String private val fileScope = CoroutineScope(Job() + Dispatchers.IO) @@ -61,21 +63,21 @@ class MangaCoverFetcher( if (options.extras.getOrDefault(USE_CUSTOM_COVER_KEY)) { val customCoverFile = customCoverFileLazy.value if (customCoverFile.exists()) { - setRatioAndColorsInScope(manga, UniFile.fromFile(customCoverFile)) + setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(customCoverFile)) return fileLoader(customCoverFile) } } - url = manga.thumbnail_url ?: error("No cover specified") + if (url == null) error("No cover specified") return when (getResourceType(url)) { Type.URL -> httpLoader() Type.File -> { val file = File(url.substringAfter("file://")) - setRatioAndColorsInScope(manga, UniFile.fromFile(file)) + setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(file)) fileLoader(file) } Type.URI -> { - setRatioAndColorsInScope(manga, UniFile.fromUri(options.context, url.toUri())) + setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromUri(options.context, url.toUri())) fileUriLoader(url) } null -> error("Invalid image") @@ -109,10 +111,10 @@ class MangaCoverFetcher( private suspend fun httpLoader(): FetchResult { val coverFile = coverFileLazy.value if (coverFile?.exists() == true && options.diskCachePolicy.readEnabled) { - if (!manga.favorite) { + if (!isInLibrary) { coverFile.setLastModified(Date().time) } - setRatioAndColorsInScope(manga, UniFile.fromFile(coverFile)) + setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(coverFile)) return fileLoader(coverFile) } @@ -123,12 +125,12 @@ class MangaCoverFetcher( val snapshotCoverCache = moveSnapshotToCoverCache(snapshot, coverFile) if (snapshotCoverCache != null) { // Read from cover cache after added to library - setRatioAndColorsInScope(manga, UniFile.fromFile(snapshotCoverCache)) + setRatioAndColorsInScope(mangaId, url, isInLibrary, UniFile.fromFile(snapshotCoverCache)) return fileLoader(snapshotCoverCache) } // Read from snapshot - setRatioAndColorsInScope(manga) + setRatioAndColorsInScope(mangaId, url, isInLibrary) return SourceFetchResult( source = snapshot.toImageSource(), mimeType = "image/*", @@ -142,7 +144,7 @@ class MangaCoverFetcher( try { // Read from cover cache after library manga cover updated val responseCoverCache = writeResponseToCoverCache(response, coverFile) - setRatioAndColorsInScope(manga) + setRatioAndColorsInScope(mangaId, url, isInLibrary) if (responseCoverCache != null) { return fileLoader(responseCoverCache) } @@ -185,7 +187,7 @@ class MangaCoverFetcher( private fun newRequest(): Request { val request = Request.Builder().apply { - url(url) + url(url!!) val sourceHeaders = sourceLazy.value?.headers if (sourceHeaders != null) @@ -293,9 +295,9 @@ class MangaCoverFetcher( ) } - private fun setRatioAndColorsInScope(manga: Manga, ogFile: UniFile? = null, force: Boolean = false) { + private fun setRatioAndColorsInScope(mangaId: Long?, mangaThumbnailUrl: String?, isInLibrary: Boolean, ogFile: UniFile? = null, force: Boolean = false) { fileScope.launch { - MangaCoverMetadata.setRatioAndColors(manga, ogFile, force) + MangaCoverMetadata.setRatioAndColors(mangaId, mangaThumbnailUrl, isInLibrary, ogFile, force) } } @@ -324,7 +326,7 @@ class MangaCoverFetcher( } } - class Factory( + class MangaFactory( private val callFactoryLazy: Lazy, ) : Fetcher.Factory { @@ -333,7 +335,9 @@ class MangaCoverFetcher( override fun create(data: Manga, options: Options, imageLoader: ImageLoader): Fetcher { return MangaCoverFetcher( - manga = data, + mangaId = data.id, + url = data.thumbnail_url, + isInLibrary = data.favorite, sourceLazy = lazy { sourceManager.get(data.source) as? HttpSource }, options = options, callFactoryLazy = callFactoryLazy, @@ -345,6 +349,29 @@ class MangaCoverFetcher( } } + class MangaCoverFactory( + private val callFactoryLazy: Lazy, + ) : Fetcher.Factory { + + private val coverCache: CoverCache by injectLazy() + private val sourceManager: SourceManager by injectLazy() + + override fun create(data: MangaCover, options: Options, imageLoader: ImageLoader): Fetcher { + return MangaCoverFetcher( + mangaId = data.mangaId, + url = data.url, + isInLibrary = data.inLibrary, + sourceLazy = lazy { sourceManager.get(data.sourceId) as? HttpSource }, + options = options, + callFactoryLazy = callFactoryLazy, + diskCacheKeyLazy = lazy { imageLoader.components.key(data, options)!! }, + coverFileLazy = lazy { coverCache.getCoverFile(data.url, !data.inLibrary) }, + customCoverFileLazy = lazy { coverCache.getCustomCoverFile(data.mangaId) }, + imageLoader = imageLoader, + ) + } + } + private enum class Type { File, URL, URI; } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt index fabc8af48d..ddf2e930b6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt @@ -2,11 +2,15 @@ package eu.kanade.tachiyomi.data.coil import coil3.key.Keyer import coil3.request.Options +import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.models.hasCustomCover import eu.kanade.tachiyomi.domain.manga.models.Manga import eu.kanade.tachiyomi.util.storage.DiskUtil +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import yokai.domain.manga.models.MangaCover -class MangaCoverKeyer : Keyer { +class MangaKeyer : Keyer { override fun key(data: Manga, options: Options): String { val key = when { data.hasCustomCover() -> data.id @@ -17,3 +21,15 @@ class MangaCoverKeyer : Keyer { return "${key};${data.cover_last_modified}" } } + +class MangaCoverKeyer(private val coverCache: CoverCache = Injekt.get()) : Keyer { + override fun key(data: MangaCover, options: Options): String { + val key = when { + coverCache.getCustomCoverFile(data.mangaId).exists() -> data.mangaId + data.inLibrary -> DiskUtil.hashKeyForDisk(data.url) + else -> data.url + } + + return "${key};${data.lastModified}" + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt index 1eac65cab4..fb560da49c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt @@ -23,6 +23,7 @@ import uy.kohesive.injekt.injectLazy import yokai.data.updateStrategyAdapter import yokai.domain.chapter.interactor.GetChapter import yokai.domain.manga.interactor.UpdateManga +import yokai.domain.manga.models.MangaCover import yokai.domain.manga.models.MangaUpdate import yokai.i18n.MR import yokai.util.lang.getString @@ -175,6 +176,12 @@ var Manga.dominantCoverColors: Pair? MangaCoverMetadata.addCoverColor(this, value.first, value.second) } +var Manga.vibrantCoverColor: Int? + get() = MangaCoverMetadata.getVibrantColor(id) + set(value) { + id?.let { MangaCoverMetadata.setVibrantColor(it, value) } + } + fun Manga.Companion.create(source: Long) = MangaImpl().apply { this.source = source } @@ -269,3 +276,7 @@ suspend fun Manga.updateCoverLastModified(updateManga: UpdateManga = Injekt.get( cover_last_modified = System.currentTimeMillis() updateManga.await(MangaUpdate(id = id!!, coverLastModified = cover_last_modified)) } + +suspend fun MangaCover.updateCoverLastModified(updateManga: UpdateManga = Injekt.get()) { + updateManga.await(MangaUpdate(id = mangaId!!, coverLastModified = System.currentTimeMillis())) +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt index ac6dc434ac..155255cc72 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt @@ -25,6 +25,7 @@ import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.view.backgroundColor import eu.kanade.tachiyomi.util.view.setCards import eu.kanade.tachiyomi.widget.AutofitRecyclerView +import yokai.domain.manga.models.cover /** * Class used to hold the displayed data of a manga in the library, like the cover or the title. @@ -125,7 +126,7 @@ class LibraryGridHolder( private fun setCover(manga: Manga) { if ((adapter.recyclerView.context as? Activity)?.isDestroyed == true) return - binding.coverThumbnail.loadManga(manga) { + binding.coverThumbnail.loadManga(manga.cover()) { val hasRatio = binding.coverThumbnail.layoutParams.height != ViewGroup.LayoutParams.WRAP_CONTENT if (!fixedSize && !hasRatio) { precision(Precision.INEXACT) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt index 43036c8a25..a7b11d2faf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt @@ -5,15 +5,14 @@ import android.view.ViewGroup import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import coil3.dispose -import eu.kanade.tachiyomi.R -import yokai.i18n.MR -import yokai.util.lang.getString -import dev.icerock.moko.resources.compose.stringResource import eu.kanade.tachiyomi.data.coil.loadManga import eu.kanade.tachiyomi.databinding.MangaListItemBinding import eu.kanade.tachiyomi.util.lang.highlightText import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.view.setCards +import yokai.domain.manga.models.cover +import yokai.i18n.MR +import yokai.util.lang.getString /** * Class used to hold the displayed data of a manga in the library, like the cover or the binding.title. @@ -97,7 +96,7 @@ class LibraryListHolder( // Update the cover. binding.coverThumbnail.dispose() - binding.coverThumbnail.loadManga(item.manga) + binding.coverThumbnail.loadManga(item.manga.cover()) } override fun onActionStateChanged(position: Int, actionState: Int) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index fe64335040..a2a355649e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -1633,7 +1633,7 @@ class LibraryPresenter( ) { val libraryManga = getManga.awaitFavorites() libraryManga.forEach { manga -> - try { withUIContext { MangaCoverMetadata.setRatioAndColors(manga) } } catch (_: Exception) { } + try { withUIContext { MangaCoverMetadata.setRatioAndColors(manga.id, manga.thumbnail_url, manga.favorite) } } catch (_: Exception) { } } MangaCoverMetadata.savePrefs() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt index 76d115ce3c..551727ec5e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt @@ -41,6 +41,7 @@ import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy +import yokai.domain.manga.models.cover import yokai.i18n.MR import yokai.util.lang.getString import android.R as AR @@ -98,7 +99,7 @@ class EditMangaDialog : DialogController { fun onViewCreated() { val context = binding.root.context - binding.mangaCover.loadManga(manga) + binding.mangaCover.loadManga(manga.cover()) val isLocal = manga.isLocal() binding.mangaLang.isVisible = isLocal diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index 50c4fba49e..3fb256fb0c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -61,6 +61,7 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.seriesType +import eu.kanade.tachiyomi.data.database.models.vibrantCoverColor import eu.kanade.tachiyomi.data.download.DownloadJob import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.notification.NotificationReceiver diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt index d25db0aec0..9f70defca5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt @@ -47,6 +47,7 @@ import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.isInNightMode import eu.kanade.tachiyomi.util.system.isLTR import eu.kanade.tachiyomi.util.view.resetStrokeColor +import yokai.domain.manga.models.cover import yokai.i18n.MR import yokai.util.lang.getString import android.R as AR @@ -671,7 +672,7 @@ class MangaHeaderHolder( if (!manga.initialized) return val drawable = adapter.controller.binding.mangaCoverFull.drawable binding.mangaCover.loadManga( - manga, + manga.cover(), builder = { placeholder(drawable) error(drawable) @@ -680,7 +681,7 @@ class MangaHeaderHolder( }, ) binding.backdrop.loadManga( - manga, + manga.cover(), builder = { placeholder(drawable) error(drawable) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaHolder.kt index 0149631070..2cf126d4eb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaHolder.kt @@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.coil.loadManga import eu.kanade.tachiyomi.databinding.MangaListItemBinding import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.util.view.setCards +import yokai.domain.manga.models.cover class MangaHolder( view: View, @@ -29,6 +30,6 @@ class MangaHolder( // Update the cover. binding.coverThumbnail.dispose() - binding.coverThumbnail.loadManga(item.manga) + binding.coverThumbnail.loadManga(item.manga.cover()) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaHolder.kt index 5fae0db110..8df1d0e4ff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaHolder.kt @@ -16,9 +16,6 @@ import androidx.core.view.updatePaddingRelative import androidx.transition.TransitionManager import androidx.transition.TransitionSet import eu.kanade.tachiyomi.R -import yokai.i18n.MR -import yokai.util.lang.getString -import dev.icerock.moko.resources.compose.stringResource import eu.kanade.tachiyomi.data.coil.loadManga import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.ChapterHistory @@ -37,6 +34,9 @@ import eu.kanade.tachiyomi.util.view.setAnimVectorCompat import eu.kanade.tachiyomi.util.view.setCards import java.util.* import java.util.concurrent.* +import yokai.domain.manga.models.cover +import yokai.i18n.MR +import yokai.util.lang.getString import android.R as AR class RecentMangaHolder( @@ -213,7 +213,7 @@ class RecentMangaHolder( else -> context.timeSpanFromNow(MR.strings.read_, item.mch.history.last_read) } if ((context as? Activity)?.isDestroyed != true) { - binding.coverThumbnail.loadManga(item.mch.manga) + binding.coverThumbnail.loadManga(item.mch.manga.cover()) } if (!item.mch.manga.isLocal()) { notifyStatus( diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaCoverMetadata.kt b/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaCoverMetadata.kt index 768d598196..111b3f0765 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaCoverMetadata.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaCoverMetadata.kt @@ -6,7 +6,6 @@ import androidx.palette.graphics.Palette import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.data.cache.CoverCache 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 java.util.concurrent.ConcurrentHashMap @@ -16,6 +15,7 @@ import uy.kohesive.injekt.injectLazy object MangaCoverMetadata { private var coverRatioMap = ConcurrentHashMap() private var coverColorMap = ConcurrentHashMap>() + private var vibrantCoverColorMap = ConcurrentHashMap() private val preferences by injectLazy() private val coverCache by injectLazy() @@ -49,19 +49,19 @@ object MangaCoverMetadata { ) } - fun setRatioAndColors(manga: Manga, ogFile: UniFile? = null, force: Boolean = false) { - if (!manga.favorite) { - remove(manga) + fun setRatioAndColors(mangaId: Long?, mangaThumbnailUrl: String?, isInLibrary: Boolean, ogFile: UniFile? = null, force: Boolean = false) { + if (!isInLibrary) { + remove(mangaId) } - if (manga.vibrantCoverColor != null && !manga.favorite) return + if (getVibrantColor(mangaId) != null && !isInLibrary) return val file = ogFile - ?: UniFile.fromFile(coverCache.getCustomCoverFile(manga))?.takeIf { it.exists() } - ?: UniFile.fromFile(coverCache.getCoverFile(manga.thumbnail_url, !manga.favorite)) + ?: UniFile.fromFile(coverCache.getCustomCoverFile(mangaId))?.takeIf { it.exists() } + ?: UniFile.fromFile(coverCache.getCoverFile(mangaThumbnailUrl, !isInLibrary)) // if the file exists and the there was still an error then the file is corrupted if (file?.exists() == true) { val options = BitmapFactory.Options() - val hasVibrantColor = if (manga.favorite) manga.vibrantCoverColor != null else true - if (manga.dominantCoverColors != null && hasVibrantColor && !force) { + val hasVibrantColor = if (isInLibrary) vibrantCoverColorMap[mangaId] != null else true + if (getColors(mangaId) != null && hasVibrantColor && !force) { options.inJustDecodeBounds = true } else { options.inSampleSize = 4 @@ -70,45 +70,74 @@ object MangaCoverMetadata { if (bitmap != null) { Palette.from(bitmap).generate { if (it == null) return@generate - if (manga.favorite) { + if (isInLibrary) { it.dominantSwatch?.let { swatch -> - manga.dominantCoverColors = swatch.rgb to swatch.titleTextColor + addCoverColor(mangaId, swatch.rgb, swatch.titleTextColor) } } val color = it.getBestColor() ?: return@generate - manga.vibrantCoverColor = color + setVibrantColor(mangaId, color) } } - if (manga.favorite && !(options.outWidth == -1 || options.outHeight == -1)) { - addCoverRatio(manga, options.outWidth / options.outHeight.toFloat()) + if (isInLibrary && !(options.outWidth == -1 || options.outHeight == -1)) { + addCoverRatio(mangaId, options.outWidth / options.outHeight.toFloat()) } } } fun remove(manga: Manga) { - val id = manga.id ?: return - coverRatioMap.remove(id) - coverColorMap.remove(id) + remove(manga.id) + } + + fun remove(mangaId: Long?) { + mangaId ?: return + coverRatioMap.remove(mangaId) + coverColorMap.remove(mangaId) } fun addCoverRatio(manga: Manga, ratio: Float) { - val id = manga.id ?: return - coverRatioMap[id] = ratio + addCoverRatio(manga.id, ratio) + } + + fun addCoverRatio(mangaId: Long?, ratio: Float) { + mangaId ?: return + coverRatioMap[mangaId] = ratio } fun addCoverColor(manga: Manga, @ColorInt color: Int, @ColorInt textColor: Int) { - val id = manga.id ?: return - coverColorMap[id] = color to textColor + addCoverColor(manga.id, color, textColor) } - fun getColors(manga: Manga): Pair? { - return coverColorMap[manga.id] + fun addCoverColor(mangaId: Long?, @ColorInt color: Int, @ColorInt textColor: Int) { + mangaId ?: return + coverColorMap[mangaId] = color to textColor + } + + fun getColors(manga: Manga): Pair? = getColors(manga.id) + + fun getColors(mangaId: Long?): Pair? { + return coverColorMap[mangaId] } fun getRatio(manga: Manga): Float? { return coverRatioMap[manga.id] } + fun setVibrantColor(mangaId: Long?, @ColorInt color: Int?) { + mangaId ?: return + + if (color == null) { + vibrantCoverColorMap.remove(mangaId) + return + } + + vibrantCoverColorMap[mangaId] = color + } + + fun getVibrantColor(mangaId: Long?): Int? { + return vibrantCoverColorMap[mangaId] + } + fun savePrefs() { val mapCopy = coverRatioMap.toMap() preferences.coverRatios().set(mapCopy.map { "${it.key}|${it.value}" }.toSet()) diff --git a/domain/src/commonMain/kotlin/eu/kanade/tachiyomi/domain/manga/models/Manga.kt b/domain/src/commonMain/kotlin/eu/kanade/tachiyomi/domain/manga/models/Manga.kt index 7f4c3eb9e3..ac57b2b7f5 100644 --- a/domain/src/commonMain/kotlin/eu/kanade/tachiyomi/domain/manga/models/Manga.kt +++ b/domain/src/commonMain/kotlin/eu/kanade/tachiyomi/domain/manga/models/Manga.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.domain.manga.models import eu.kanade.tachiyomi.source.model.SManga import java.util.Locale -import kotlin.collections.set import yokai.domain.manga.models.MangaUpdate // TODO: Transform into data class @@ -197,12 +196,6 @@ interface Manga : SManga { get() = chapter_flags and CHAPTER_SORTING_MASK set(sort) = setChapterFlags(sort, CHAPTER_SORTING_MASK) - var vibrantCoverColor: Int? - get() = vibrantCoverColorMap[id] - set(value) { - id?.let { vibrantCoverColorMap[it] = value } - } - fun toMangaUpdate(): MangaUpdate { return MangaUpdate( id = id!!, @@ -268,7 +261,5 @@ interface Manga : SManga { const val TYPE_MANHUA = 3 const val TYPE_COMIC = 4 const val TYPE_WEBTOON = 5 - - private val vibrantCoverColorMap: HashMap = hashMapOf() } } diff --git a/domain/src/commonMain/kotlin/yokai/domain/manga/models/Manga.kt b/domain/src/commonMain/kotlin/yokai/domain/manga/models/Manga.kt index 1c41f77063..90f3307faa 100644 --- a/domain/src/commonMain/kotlin/yokai/domain/manga/models/Manga.kt +++ b/domain/src/commonMain/kotlin/yokai/domain/manga/models/Manga.kt @@ -23,4 +23,5 @@ data class Manga( var chapterFlags: Int, var hideTitle: Boolean, var filteredScanlators: String?, + var coverLastModified: Long, ): Serializable diff --git a/domain/src/commonMain/kotlin/yokai/domain/manga/models/MangaCover.kt b/domain/src/commonMain/kotlin/yokai/domain/manga/models/MangaCover.kt new file mode 100644 index 0000000000..44c4816dda --- /dev/null +++ b/domain/src/commonMain/kotlin/yokai/domain/manga/models/MangaCover.kt @@ -0,0 +1,27 @@ +package yokai.domain.manga.models + +import eu.kanade.tachiyomi.domain.manga.models.Manga as TachiManga + +data class MangaCover( + val mangaId: Long?, + val sourceId: Long, + val url: String, + val lastModified: Long, + val inLibrary: Boolean, +) + +fun TachiManga.cover() = MangaCover( + mangaId = id, + sourceId = source, + url = thumbnail_url ?: "", + lastModified = cover_last_modified, + inLibrary = favorite, +) + +fun Manga.cover() = MangaCover( + mangaId = id, + sourceId = source, + url = thumbnailUrl ?: "", + lastModified = coverLastModified, + inLibrary = favorite, +)