From 3412806bfc48d1c277cf238415106fd858d36aa9 Mon Sep 17 00:00:00 2001 From: Ahmad Ansori Palembani Date: Thu, 23 May 2024 09:04:02 +0700 Subject: [PATCH] refactor: Switch to Coil3 --- app/build.gradle.kts | 6 +- app/src/main/java/eu/kanade/tachiyomi/App.kt | 10 ++- .../appwidget/UpdatesGridGlanceWidget.kt | 17 ++-- .../kanade/tachiyomi/data/cache/CoverCache.kt | 4 +- .../tachiyomi/data/image/coil/CoilSetup.kt | 61 ++++++-------- .../data/image/coil/CoverViewTarget.kt | 15 +++- .../image/coil/LibraryMangaImageTarget.kt | 16 ++-- .../data/image/coil/MangaCoverFetcher.kt | 82 +++++++++++-------- .../data/image/coil/MangaCoverKeyer.kt | 4 +- .../data/image/coil/TachiyomiImageDecoder.kt | 22 +++-- .../kanade/tachiyomi/data/image/coil/Utils.kt | 33 ++++---- .../data/library/LibraryUpdateJob.kt | 22 +++-- .../data/library/LibraryUpdateNotifier.kt | 15 ++-- .../tachiyomi/ui/extension/ExtensionHolder.kt | 4 +- .../tachiyomi/ui/library/LibraryGridHolder.kt | 6 +- .../tachiyomi/ui/library/LibraryListHolder.kt | 2 +- .../tachiyomi/ui/manga/EditMangaDialog.kt | 10 +-- .../ui/manga/MangaDetailsController.kt | 24 ++++-- .../ui/manga/MangaDetailsPresenter.kt | 13 ++- .../tachiyomi/ui/manga/MangaHeaderHolder.kt | 11 ++- .../ui/manga/track/TrackSearchItem.kt | 5 +- .../tachiyomi/ui/migration/MangaHolder.kt | 2 +- .../manga/process/MigrationProcessHolder.kt | 9 +- .../tachiyomi/ui/reader/SaveImageNotifier.kt | 8 +- .../ui/reader/viewer/ReaderPageImageView.kt | 35 ++++---- .../source/browse/BrowseSourceGridHolder.kt | 11 +-- .../source/browse/BrowseSourceListHolder.kt | 11 +-- .../globalsearch/GlobalSearchMangaHolder.kt | 14 ++-- .../util/manga/MangaShortcutManager.kt | 8 +- .../tachiyomi/util/system/CoilExtensions.kt | 9 ++ .../kanade/tachiyomi/util/system/ImageUtil.kt | 12 +-- .../kanade/tachiyomi/widget/GifViewTarget.kt | 8 +- gradle/libs.versions.toml | 10 ++- 33 files changed, 286 insertions(+), 233 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/util/system/CoilExtensions.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 48affbb09b..4a92b63a7a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -256,9 +256,7 @@ dependencies { implementation(libs.injekt.core) // Image library - implementation(libs.coil) - implementation(libs.coil.gif) - implementation(libs.coil.svg) + implementation(libs.bundles.coil) // Logging implementation(libs.timber) @@ -327,7 +325,7 @@ tasks { "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", - "-opt-in=coil.annotation.ExperimentalCoilApi", + "-opt-in=coil3.annotation.ExperimentalCoilApi", "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi", "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-opt-in=kotlinx.coroutines.FlowPreview", diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 824431737a..14b13d151e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -20,6 +20,9 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.multidex.MultiDex +import coil3.ImageLoader +import coil3.PlatformContext +import coil3.SingletonImageLoader import dev.yokai.domain.AppState import eu.kanade.tachiyomi.appwidget.TachiyomiWidgetManager import eu.kanade.tachiyomi.data.image.coil.CoilSetup @@ -44,7 +47,7 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.injectLazy import java.security.Security -open class App : Application(), DefaultLifecycleObserver { +open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory { val preferences: PreferencesHelper by injectLazy() @@ -71,7 +74,6 @@ open class App : Application(), DefaultLifecycleObserver { Injekt.importModule(PreferenceModule(this)) Injekt.importModule(AppModule(this)) - CoilSetup(this) setupNotificationChannels() ProcessLifecycleOwner.get().lifecycle.addObserver(this) @@ -171,6 +173,10 @@ open class App : Application(), DefaultLifecycleObserver { } } } + + override fun newImageLoader(context: PlatformContext): ImageLoader { + return CoilSetup.setup(context) + } } private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE" 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 7fb5fa0a13..e939be381e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/appwidget/UpdatesGridGlanceWidget.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/appwidget/UpdatesGridGlanceWidget.kt @@ -16,13 +16,14 @@ import androidx.glance.appwidget.provideContent import androidx.glance.appwidget.updateAll import androidx.glance.background import androidx.glance.layout.fillMaxSize -import coil.executeBlocking -import coil.imageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest -import coil.size.Precision -import coil.size.Scale -import coil.transform.RoundedCornersTransformation +import coil3.executeBlocking +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.request.transformations +import coil3.size.Precision +import coil3.size.Scale +import coil3.transform.RoundedCornersTransformation import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.appwidget.components.CoverHeight import eu.kanade.tachiyomi.appwidget.components.CoverWidth @@ -109,7 +110,7 @@ class UpdatesGridGlanceWidget : GlanceAppWidget() { } } .build() - Pair(updatesView.id!!, app.imageLoader.executeBlocking(request).drawable?.toBitmap()) + Pair(updatesView.id!!, app.imageLoader.executeBlocking(request).image?.asDrawable(app.resources)?.toBitmap()) } } 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 3a905aa254..4318389ebe 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 @@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.data.cache import android.content.Context import android.text.format.Formatter -import coil.imageLoader -import coil.memory.MemoryCache +import coil3.imageLoader +import coil3.memory.MemoryCache import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Manga diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoilSetup.kt b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoilSetup.kt index a8e44cd3f9..cf8b387bfe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoilSetup.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoilSetup.kt @@ -2,47 +2,38 @@ package eu.kanade.tachiyomi.data.image.coil import android.app.ActivityManager import android.content.Context -import android.os.Build import androidx.core.content.getSystemService -import coil.Coil -import coil.ImageLoader -import coil.decode.GifDecoder -import coil.decode.ImageDecoderDecoder -import coil.disk.DiskCache -import coil.memory.MemoryCache -import coil.util.DebugLogger -import eu.kanade.tachiyomi.BuildConfig +import coil3.ImageLoader +import coil3.disk.DiskCache +import coil3.disk.directory +import coil3.memory.MemoryCache +import coil3.network.okhttp.OkHttpNetworkFetcherFactory +import coil3.request.allowHardware +import coil3.request.allowRgb565 +import coil3.request.crossfade import eu.kanade.tachiyomi.network.NetworkHelper import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -class CoilSetup(context: Context) { - init { - val imageLoader = ImageLoader.Builder(context).apply { - val callFactoryInit = { Injekt.get().client } - val diskCacheInit = { CoilDiskCache.get(context) } - components { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - add(ImageDecoderDecoder.Factory()) - } else { - add(GifDecoder.Factory()) +class CoilSetup { + companion object { + fun setup(context: Context): ImageLoader { + return ImageLoader.Builder(context).apply { + val callFactoryLazy = lazy { Injekt.get().client } + val diskCacheLazy = lazy { CoilDiskCache.get(context) } + components { + add(OkHttpNetworkFetcherFactory(callFactoryLazy::value)) + add(TachiyomiImageDecoder.Factory()) + add(MangaCoverFetcher.Factory(callFactoryLazy, diskCacheLazy)) + add(MangaCoverKeyer()) } - add(TachiyomiImageDecoder.Factory()) - add(MangaCoverFetcher.Factory(lazy(callFactoryInit), lazy(diskCacheInit))) - add(MangaCoverKeyer()) - add(InputStreamFetcher.Factory()) - } - callFactory(callFactoryInit) - diskCache(diskCacheInit) - memoryCache { MemoryCache.Builder(context).maxSizePercent(0.40).build() } - crossfade(true) - allowRgb565(context.getSystemService()!!.isLowRamDevice) - allowHardware(true) - if (BuildConfig.DEBUG) { - logger(DebugLogger()) - } - }.build() - Coil.setImageLoader(imageLoader) + diskCache(diskCacheLazy::value) + memoryCache { MemoryCache.Builder().maxSizePercent(context, 0.40).build() } + crossfade(true) + allowRgb565(context.getSystemService()!!.isLowRamDevice) + allowHardware(true) + }.build() + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoverViewTarget.kt b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoverViewTarget.kt index 4fe176bb67..9671b379d8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoverViewTarget.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/CoverViewTarget.kt @@ -5,7 +5,8 @@ import android.view.View import android.widget.ImageView import androidx.core.view.isVisible import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat -import coil.target.ImageViewTarget +import coil3.Image +import coil3.target.ImageViewTarget import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.getResourceColor @@ -15,7 +16,9 @@ class CoverViewTarget( val scaleType: ImageView.ScaleType = ImageView.ScaleType.CENTER_CROP, ) : ImageViewTarget(view) { - override fun onError(error: Drawable?) { + override fun onError(error: Image?) { + //val drawable = error?.asDrawable(view.context.resources) + progress?.isVisible = false view.scaleType = ImageView.ScaleType.CENTER val vector = VectorDrawableCompat.create( @@ -27,13 +30,17 @@ class CoverViewTarget( view.setImageDrawable(vector) } - override fun onStart(placeholder: Drawable?) { + override fun onStart(placeholder: Image?) { + //val drawable = placeholder?.asDrawable(view.context.resources) + progress?.isVisible = true view.scaleType = scaleType super.onStart(placeholder) } - override fun onSuccess(result: Drawable) { + override fun onSuccess(result: Image) { + //val drawable = result?.asDrawable(view.context.resources) + progress?.isVisible = false view.scaleType = scaleType super.onSuccess(result) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/LibraryMangaImageTarget.kt b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/LibraryMangaImageTarget.kt index 40d067dbfa..56870980d4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/LibraryMangaImageTarget.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/LibraryMangaImageTarget.kt @@ -1,15 +1,15 @@ package eu.kanade.tachiyomi.data.image.coil import android.graphics.BitmapFactory -import android.graphics.drawable.Drawable import android.widget.ImageView import androidx.palette.graphics.Palette -import coil.ImageLoader -import coil.imageLoader -import coil.memory.MemoryCache -import coil.request.Disposable -import coil.request.ImageRequest -import coil.target.ImageViewTarget +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 import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.util.system.launchIO @@ -22,7 +22,7 @@ class LibraryMangaImageTarget( private val coverCache: CoverCache by injectLazy() - override fun onError(error: Drawable?) { + override fun onError(error: Image?) { super.onError(error) if (manga.favorite) { launchIO { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaCoverFetcher.kt index c7685f4082..3b1f0a3bc0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaCoverFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaCoverFetcher.kt @@ -1,16 +1,16 @@ package eu.kanade.tachiyomi.data.image.coil import android.webkit.MimeTypeMap -import coil.ImageLoader -import coil.decode.DataSource -import coil.decode.ImageSource -import coil.disk.DiskCache -import coil.fetch.FetchResult -import coil.fetch.Fetcher -import coil.fetch.SourceResult -import coil.network.HttpException -import coil.request.Options -import coil.request.Parameters +import coil3.Extras +import coil3.ImageLoader +import coil3.decode.DataSource +import coil3.decode.ImageSource +import coil3.disk.DiskCache +import coil3.fetch.FetchResult +import coil3.fetch.Fetcher +import coil3.fetch.SourceFetchResult +import coil3.getOrDefault +import coil3.request.Options import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.network.await @@ -26,17 +26,16 @@ import okhttp3.Call import okhttp3.Request import okhttp3.Response import okhttp3.internal.closeQuietly +import okio.FileSystem import okio.Path.Companion.toOkioPath import okio.Source import okio.buffer import okio.sink -import okio.source import timber.log.Timber -import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.io.File import java.net.HttpURLConnection -import java.util.Date +import java.util.* class MangaCoverFetcher( private val manga: Manga, @@ -71,7 +70,7 @@ class MangaCoverFetcher( val networkRead = options.networkCachePolicy.readEnabled val onlyCache = !networkRead && diskRead val shouldFetchRemotely = networkRead && !diskRead && !onlyCache - val useCustomCover = options.parameters.value(useCustomCover) ?: true + val useCustomCover = options.extras.getOrDefault(USE_CUSTOM_COVER_KEY) // Use custom cover if exists if (!shouldFetchRemotely) { val customCoverFile by lazy { coverCache.getCustomCoverFile(manga) } @@ -101,7 +100,7 @@ class MangaCoverFetcher( // Read from snapshot setRatioAndColorsInScope(manga) - return SourceResult( + return SourceFetchResult( source = snapshot.toImageSource(), mimeType = "image/*", dataSource = DataSource.DISK, @@ -122,7 +121,7 @@ class MangaCoverFetcher( // Read from disk cache snapshot = writeToDiskCache(snapshot, response) if (snapshot != null) { - return SourceResult( + return SourceFetchResult( source = snapshot.toImageSource(), mimeType = "image/*", dataSource = DataSource.NETWORK, @@ -130,8 +129,8 @@ class MangaCoverFetcher( } // Read from response if cache is unused or unusable - return SourceResult( - source = ImageSource(source = responseBody.source(), context = options.context), + return SourceFetchResult( + source = ImageSource(source = responseBody.source(), fileSystem = FileSystem.SYSTEM), mimeType = "image/*", dataSource = if (response.cacheResponse != null) DataSource.DISK else DataSource.NETWORK, ) @@ -149,18 +148,20 @@ class MangaCoverFetcher( val client = sourceLazy.value?.client ?: callFactoryLazy.value val response = client.newCall(newRequest()).await() if (!response.isSuccessful && response.code != HttpURLConnection.HTTP_NOT_MODIFIED) { - response.body?.closeQuietly() - throw HttpException(response) + response.body.closeQuietly() + throw Exception(response.message) // FIXME: Should probably use something else other than generic Exception } return response } private fun newRequest(): Request { - val request = Request.Builder() - .url(url) - .headers(sourceLazy.value?.headers ?: options.headers) - // Support attaching custom data to the network request. - .tag(Parameters::class.java, options.parameters) + val request = Request.Builder().apply { + url(url) + + val sourceHeaders = sourceLazy.value?.headers + if (sourceHeaders != null) + headers(sourceHeaders) + } val diskRead = options.diskCachePolicy.readEnabled val networkRead = options.networkCachePolicy.readEnabled @@ -227,7 +228,11 @@ class MangaCoverFetcher( } private fun readFromDiskCache(): DiskCache.Snapshot? { - return if (options.diskCachePolicy.readEnabled) diskCacheLazy.value[diskCacheKey!!] else null + return if (options.diskCachePolicy.readEnabled) { + diskCacheLazy.value.openSnapshot(diskCacheKey!!) + } else { + null + } } private fun writeToDiskCache( @@ -239,15 +244,15 @@ class MangaCoverFetcher( return null } val editor = if (snapshot != null) { - snapshot.closeAndEdit() + snapshot.closeAndOpenEditor() } else { - diskCacheLazy.value.edit(diskCacheKey!!) + diskCacheLazy.value.openEditor(diskCacheKey!!) } ?: return null try { diskCacheLazy.value.fileSystem.write(editor.data) { - response.body!!.source().readAll(this) + response.body.source().readAll(this) } - return editor.commitAndGet() + return editor.commitAndOpenSnapshot() } catch (e: Exception) { try { editor.abort() @@ -258,7 +263,12 @@ class MangaCoverFetcher( } private fun DiskCache.Snapshot.toImageSource(): ImageSource { - return ImageSource(file = data, diskCacheKey = diskCacheKey, closeable = this) + return ImageSource( + file = data, + fileSystem = FileSystem.SYSTEM, + diskCacheKey = diskCacheKey, + closeable = this, + ) } private fun setRatioAndColorsInScope(manga: Manga, ogFile: File? = null, force: Boolean = false) { @@ -283,8 +293,12 @@ class MangaCoverFetcher( } private fun fileLoader(file: File): FetchResult { - return SourceResult( - source = ImageSource(file = file.toOkioPath(), diskCacheKey = diskCacheKey), + return SourceFetchResult( + source = ImageSource( + file = file.toOkioPath(), + fileSystem = FileSystem.SYSTEM, + diskCacheKey = diskCacheKey, + ), mimeType = "image/*", dataSource = DataSource.DISK, ) @@ -318,7 +332,7 @@ class MangaCoverFetcher( } companion object { - const val useCustomCover = "use_custom_cover" + val USE_CUSTOM_COVER_KEY = Extras.Key(true) private val CACHE_CONTROL_FORCE_NETWORK_NO_CACHE = CacheControl.Builder().noCache().noStore().build() private val CACHE_CONTROL_NO_NETWORK_NO_CACHE = CacheControl.Builder().noCache().onlyIfCached().build() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaCoverKeyer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaCoverKeyer.kt index 221b3b5a96..f71c3800eb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaCoverKeyer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/MangaCoverKeyer.kt @@ -1,7 +1,7 @@ package eu.kanade.tachiyomi.data.image.coil -import coil.key.Keyer -import coil.request.Options +import coil3.key.Keyer +import coil3.request.Options import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.util.storage.DiskUtil diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/TachiyomiImageDecoder.kt index 330b82b17b..748e433dc2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/TachiyomiImageDecoder.kt @@ -2,11 +2,15 @@ package eu.kanade.tachiyomi.data.image.coil import android.graphics.Bitmap import android.os.Build -import androidx.core.graphics.drawable.toDrawable -import coil.ImageLoader -import coil.decode.* -import coil.fetch.SourceResult -import coil.request.Options +import coil3.ImageLoader +import coil3.asCoilImage +import coil3.decode.DecodeResult +import coil3.decode.DecodeUtils +import coil3.decode.Decoder +import coil3.decode.ImageSource +import coil3.fetch.SourceFetchResult +import coil3.request.Options +import coil3.request.bitmapConfig import eu.kanade.tachiyomi.util.system.GLUtil import eu.kanade.tachiyomi.util.system.ImageUtil import okio.BufferedSource @@ -79,16 +83,16 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti */ return DecodeResult( - drawable = bitmap.toDrawable(options.context.resources), + image = bitmap.asCoilImage(), isSampled = sampleSize > 1, ) } class Factory : Decoder.Factory { - override fun create(result: SourceResult, options: Options, imageLoader: ImageLoader): Decoder? { - if (isApplicable(result.source.source()) || options.customDecoder) return TachiyomiImageDecoder(result.source, options) - return null + override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? { + if (!isApplicable(result.source.source())) return null + return TachiyomiImageDecoder(result.source, options) } private fun isApplicable(source: BufferedSource): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/Utils.kt b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/Utils.kt index 9c5718a29e..311d83b65f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/Utils.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/image/coil/Utils.kt @@ -1,14 +1,14 @@ package eu.kanade.tachiyomi.data.image.coil -import android.graphics.Bitmap -import android.os.Build -import coil.request.ImageRequest -import coil.request.Options -import coil.size.Dimension -import coil.size.Scale -import coil.size.Size -import coil.size.isOriginal -import coil.size.pxOrElse +import coil3.Extras +import coil3.getExtra +import coil3.request.ImageRequest +import coil3.request.Options +import coil3.size.Dimension +import coil3.size.Scale +import coil3.size.Size +import coil3.size.isOriginal +import coil3.size.pxOrElse internal inline fun Size.widthPx(scale: Scale, original: () -> Int): Int { return if (isOriginal) original() else width.toPx(scale) @@ -26,22 +26,19 @@ internal fun Dimension.toPx(scale: Scale): Int = pxOrElse { } fun ImageRequest.Builder.cropBorders(enable: Boolean) = apply { - setParameter(cropBordersKey, enable) + extras[cropBordersKey] = enable } val Options.cropBorders: Boolean - get() = parameters.value(cropBordersKey) ?: false + get() = getExtra(cropBordersKey) -private val cropBordersKey = "crop_borders" +private val cropBordersKey = Extras.Key(default = false) fun ImageRequest.Builder.customDecoder(enable: Boolean) = apply { - setParameter(customDecoderKey, enable) + extras[customDecoderKey] = enable } val Options.customDecoder: Boolean - get() = parameters.value(customDecoderKey) ?: false + get() = getExtra(customDecoderKey) -private val customDecoderKey = "custom_decoder" - -val Options.bitmapConfig: Bitmap.Config - get() = this.config +private val customDecoderKey = Extras.Key(default = false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt index a07997d56a..744145a357 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt @@ -16,9 +16,9 @@ import androidx.work.WorkInfo import androidx.work.WorkManager import androidx.work.WorkQuery import androidx.work.WorkerParameters -import coil.Coil -import coil.request.CachePolicy -import coil.request.ImageRequest +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.DatabaseHelper @@ -261,21 +261,19 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet val thumbnailUrl = manga.thumbnail_url manga.copyFrom(networkManga) manga.initialized = true - if (thumbnailUrl != manga.thumbnail_url) { - coverCache.deleteFromCache(thumbnailUrl) - // load new covers in background - val request = + val request: ImageRequest = + if (thumbnailUrl != manga.thumbnail_url) { + coverCache.deleteFromCache(thumbnailUrl) + // load new covers in background ImageRequest.Builder(context).data(manga) .memoryCachePolicy(CachePolicy.DISABLED).build() - Coil.imageLoader(context).execute(request) - } else { - val request = + } else { ImageRequest.Builder(context).data(manga) .memoryCachePolicy(CachePolicy.DISABLED) .diskCachePolicy(CachePolicy.WRITE_ONLY) .build() - Coil.imageLoader(context).execute(request) - } + } + context.imageLoader.execute(request) db.insertManga(manga).executeAsBlocking() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index 8a1831dec3..084fe7a8be 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -13,10 +13,11 @@ import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat -import coil.Coil -import coil.request.CachePolicy -import coil.request.ImageRequest -import coil.transform.CircleCropTransformation +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.request.transformations +import coil3.transform.CircleCropTransformation import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.LibraryManga @@ -191,11 +192,11 @@ class LibraryUpdateNotifier(private val context: Context) { .transformations(CircleCropTransformation()) .size(width = ICON_SIZE, height = ICON_SIZE).build() - Coil.imageLoader(context) - .execute(request).drawable?.let { drawable -> + context.imageLoader + .execute(request).image?.asDrawable(context.resources)?.let { drawable -> setLargeIcon((drawable as? BitmapDrawable)?.bitmap) } - } catch (e: Exception) { + } catch (_: Exception) { } setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) setContentTitle(manga.title) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionHolder.kt index fc1fbafc63..799126ca3c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionHolder.kt @@ -9,8 +9,8 @@ import androidx.core.text.color import androidx.core.text.scale import androidx.core.view.isGone import androidx.core.view.isVisible -import coil.dispose -import coil.load +import coil3.dispose +import coil3.load import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget import eu.kanade.tachiyomi.databinding.ExtensionCardItemBinding 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 3d085d0b0e..4e591a2e62 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 @@ -10,9 +10,9 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible import androidx.core.view.marginBottom import androidx.core.view.updateLayoutParams -import coil.dispose -import coil.size.Precision -import coil.size.Scale +import coil3.dispose +import coil3.size.Precision +import coil3.size.Scale import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.image.coil.loadManga 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 5099c3ff77..8ddd2d30e5 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 @@ -4,7 +4,7 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams -import coil.dispose +import coil3.dispose import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.image.coil.loadManga import eu.kanade.tachiyomi.databinding.MangaListItemBinding 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 c618eec2bd..611e0d1b01 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 @@ -13,8 +13,7 @@ import android.view.inputmethod.InputMethodManager import androidx.core.graphics.ColorUtils import androidx.core.view.children import androidx.core.view.isVisible -import coil.load -import coil.request.Parameters +import coil3.load import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipGroup import eu.kanade.tachiyomi.R @@ -215,10 +214,9 @@ class EditMangaDialog : DialogController { binding.resetCover.setOnClickListener { binding.mangaCover.load( manga, - builder = { - parameters(Parameters.Builder().set(MangaCoverFetcher.useCustomCover, false).build()) - }, - ) + ) { + extras[MangaCoverFetcher.USE_CUSTOM_COVER_KEY] = false + } customCoverUri = null willResetCover = true } 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 48fead9445..62a85f929f 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 @@ -7,6 +7,7 @@ import android.app.PendingIntent import android.content.ClipData import android.content.Context import android.content.Intent +import android.graphics.Bitmap import android.graphics.Color import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Icon @@ -40,8 +41,9 @@ import androidx.palette.graphics.Palette import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import coil.imageLoader -import coil.request.ImageRequest +import coil3.imageLoader +import coil3.request.ImageRequest +import coil3.request.allowHardware import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import com.google.android.material.chip.Chip @@ -569,8 +571,20 @@ class MangaDetailsController : val request = ImageRequest.Builder(view.context).data(presenter.manga).allowHardware(false) .memoryCacheKey(presenter.manga.key()) .target( - onSuccess = { drawable -> - val bitmap = (drawable as? BitmapDrawable)?.bitmap + onSuccess = { image -> + val drawable = image.asDrawable(view.context.resources) + + val copy = (drawable as? BitmapDrawable)?.let { + BitmapDrawable( + view.context.resources, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + it.bitmap.copy(Bitmap.Config.HARDWARE, false) + else + it.bitmap.copy(it.bitmap.config, false), + ) + } ?: drawable + + val bitmap = (copy as? BitmapDrawable)?.bitmap // Generate the Palette on a background thread. if (bitmap != null) { Palette.from(bitmap).generate { @@ -590,7 +604,7 @@ class MangaDetailsController : } } } - binding.mangaCoverFull.setImageDrawable(drawable) + binding.mangaCoverFull.setImageDrawable(copy) getHeader()?.updateCover(manga!!) }, onError = { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt index 9a89552f0f..dcaa7293e2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt @@ -4,12 +4,11 @@ import android.app.Application import android.graphics.Bitmap import android.net.Uri import android.os.Environment -import coil.Coil -import coil.imageLoader -import coil.memory.MemoryCache -import coil.request.CachePolicy -import coil.request.ImageRequest -import coil.request.SuccessResult +import coil3.imageLoader +import coil3.memory.MemoryCache +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.request.SuccessResult import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.DatabaseHelper @@ -379,7 +378,7 @@ class MangaDetailsPresenter( .diskCachePolicy(CachePolicy.WRITE_ONLY) .build() - if (Coil.imageLoader(preferences.context).execute(request) is SuccessResult) { + if (preferences.context.imageLoader.execute(request) is SuccessResult) { preferences.context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key())) withContext(Dispatchers.Main) { view?.setPaletteColor() 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 a6d45bb274..ef5449f968 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 @@ -25,7 +25,9 @@ import androidx.core.view.updateLayoutParams import androidx.core.widget.TextViewCompat import androidx.transition.TransitionSet import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat -import coil.request.CachePolicy +import coil3.request.CachePolicy +import coil3.request.placeholder +import coil3.request.error import com.google.android.material.button.MaterialButton import com.google.android.material.chip.Chip import eu.kanade.tachiyomi.R @@ -289,7 +291,7 @@ class MangaHeaderHolder( } } - @SuppressLint("SetTextI18n") + @SuppressLint("SetTextI18n", "StringFormatInvalid") fun bind(item: MangaHeaderItem, manga: Manga) { val presenter = adapter.delegate.mangaPresenter() if (binding == null) { @@ -680,9 +682,10 @@ class MangaHeaderHolder( diskCachePolicy(CachePolicy.READ_ONLY) target( onSuccess = { - val bitmap = (it as? BitmapDrawable)?.bitmap + val result = it.asDrawable(itemView.resources) + val bitmap = (result as? BitmapDrawable)?.bitmap if (bitmap == null) { - binding.backdrop.setImageDrawable(it) + binding.backdrop.setImageDrawable(result) return@target } val yOffset = (bitmap.height / 2 * 0.33).toInt() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchItem.kt index 9b967b2995..102da326bf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchItem.kt @@ -2,8 +2,9 @@ package eu.kanade.tachiyomi.ui.manga.track import android.view.View import androidx.core.view.isVisible -import coil.dispose -import coil.load +import coil3.dispose +import coil3.load +import coil3.request.allowHardware import com.google.android.material.shape.CornerFamily import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.items.AbstractItem 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 8a49f2902b..102a359274 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 @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.migration import android.view.View import androidx.recyclerview.widget.RecyclerView -import coil.dispose +import coil3.dispose import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.tachiyomi.data.image.coil.loadManga diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcessHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcessHolder.kt index 872f62850e..7ca4e7108b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcessHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcessHolder.kt @@ -5,8 +5,8 @@ import androidx.appcompat.widget.PopupMenu import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isInvisible import androidx.core.view.isVisible -import coil.Coil -import coil.request.ImageRequest +import coil3.imageLoader +import coil3.request.ImageRequest import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Manga @@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.ui.library.setFreeformCoverRatio import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.util.system.launchUI +import eu.kanade.tachiyomi.util.system.setExtras import eu.kanade.tachiyomi.util.view.setCards import eu.kanade.tachiyomi.util.view.setVectorCompat import eu.kanade.tachiyomi.util.view.withFadeTransaction @@ -147,9 +148,9 @@ class MigrationProcessHolder( val request = ImageRequest.Builder(view.context).data(manga) .target(CoverViewTarget(coverThumbnail, progress)) - .setParameter(MangaCoverFetcher.useCustomCover, false) + .setExtras(MangaCoverFetcher.USE_CUSTOM_COVER_KEY, false) .build() - Coil.imageLoader(view.context).enqueue(request) + view.context.imageLoader.enqueue(request) compactTitle.isVisible = true gradient.isVisible = true diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt index 416a649d51..cf56551c9f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt @@ -5,9 +5,9 @@ import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat -import coil.Coil -import coil.request.CachePolicy -import coil.request.ImageRequest +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.NotificationHandler import eu.kanade.tachiyomi.data.notification.NotificationReceiver @@ -51,7 +51,7 @@ class SaveImageNotifier(private val context: Context) { } }, ).build() - Coil.imageLoader(context).enqueue(request) + context.imageLoader.enqueue(request) } private fun showCompleteNotification(file: File, image: Bitmap) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt index 3f87f74b6a..856e602363 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt @@ -18,12 +18,14 @@ import androidx.annotation.CallSuper import androidx.annotation.StyleRes import androidx.appcompat.widget.AppCompatImageView import androidx.core.view.isVisible -import coil.dispose -import coil.imageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest -import coil.size.Precision -import coil.size.ViewSizeResolver +import coil3.BitmapImage +import coil3.dispose +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.request.crossfade +import coil3.size.Precision +import coil3.size.ViewSizeResolver import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE @@ -180,7 +182,7 @@ open class ReaderPageImageView @JvmOverloads constructor( } private fun setNonAnimatedImage( - image: Any, + data: Any, config: Config, ) = (pageView as? SubsamplingScaleImageView)?.apply { setDoubleTapZoomDuration(config.zoomDuration.getSystemScaledDuration()) @@ -226,13 +228,13 @@ open class ReaderPageImageView @JvmOverloads constructor( val useCoilPipeline = false // FIXME: "Bitmap too large to be uploaded into a texture" if (isWebtoon && useCoilPipeline) { val request = ImageRequest.Builder(context) - .data(image) + .data(data) .memoryCachePolicy(CachePolicy.DISABLED) .diskCachePolicy(CachePolicy.DISABLED) .target( onSuccess = { result -> - val drawable = result as BitmapDrawable - setImage(ImageSource.bitmap(drawable.bitmap)) + val image = result as BitmapDrawable + setImage(ImageSource.bitmap(image.bitmap)) isVisible = true }, onError = { @@ -247,10 +249,10 @@ open class ReaderPageImageView @JvmOverloads constructor( .build() context.imageLoader.enqueue(request) } else { - when (image) { - is BitmapDrawable -> setImage(ImageSource.bitmap(image.bitmap)) - is InputStream -> setImage(ImageSource.inputStream(image)) - else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}") + when (data) { + is BitmapDrawable -> setImage(ImageSource.bitmap(data.bitmap)) + is InputStream -> setImage(ImageSource.inputStream(data)) + else -> throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}") } isVisible = true } @@ -314,8 +316,9 @@ open class ReaderPageImageView @JvmOverloads constructor( .diskCachePolicy(CachePolicy.DISABLED) .target( onSuccess = { result -> - setImageDrawable(result) - (result as? Animatable)?.start() + val drawable = result.asDrawable(context.resources) + setImageDrawable(drawable) + (drawable as? Animatable)?.start() isVisible = true this@ReaderPageImageView.onImageLoaded() }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceGridHolder.kt index c2e731cb38..bc90df7141 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceGridHolder.kt @@ -5,9 +5,9 @@ import android.view.View import androidx.core.graphics.ColorUtils import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView -import coil.Coil -import coil.dispose -import coil.request.ImageRequest +import coil3.dispose +import coil3.imageLoader +import coil3.request.ImageRequest import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.tachiyomi.data.database.models.Manga @@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget import eu.kanade.tachiyomi.data.image.coil.MangaCoverFetcher import eu.kanade.tachiyomi.databinding.MangaGridItemBinding import eu.kanade.tachiyomi.ui.library.LibraryCategoryAdapter +import eu.kanade.tachiyomi.util.system.setExtras import eu.kanade.tachiyomi.util.view.setCards /** @@ -67,9 +68,9 @@ class BrowseSourceGridHolder( manga.id ?: return val request = ImageRequest.Builder(view.context).data(manga) .target(CoverViewTarget(binding.coverThumbnail, binding.progress)) - .setParameter(MangaCoverFetcher.useCustomCover, false) + .setExtras(MangaCoverFetcher.USE_CUSTOM_COVER_KEY, false) .build() - Coil.imageLoader(view.context).enqueue(request) + view.context.imageLoader.enqueue(request) binding.coverThumbnail.alpha = if (manga.favorite) 0.34f else 1.0f binding.card.strokeColorStateList?.defaultColor?.let { color -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceListHolder.kt index 90c5d14610..920f1e91df 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceListHolder.kt @@ -3,15 +3,16 @@ package eu.kanade.tachiyomi.ui.source.browse import android.view.View import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView -import coil.Coil -import coil.dispose -import coil.request.ImageRequest +import coil3.dispose +import coil3.imageLoader +import coil3.request.ImageRequest import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget import eu.kanade.tachiyomi.data.image.coil.MangaCoverFetcher import eu.kanade.tachiyomi.databinding.MangaListItemBinding +import eu.kanade.tachiyomi.util.system.setExtras import eu.kanade.tachiyomi.util.view.setCards /** @@ -56,9 +57,9 @@ class BrowseSourceListHolder( manga.id ?: return val request = ImageRequest.Builder(view.context).data(manga) .target(CoverViewTarget(binding.coverThumbnail)) - .setParameter(MangaCoverFetcher.useCustomCover, false) + .setExtras(MangaCoverFetcher.USE_CUSTOM_COVER_KEY, false) .build() - Coil.imageLoader(view.context).enqueue(request) + view.context.imageLoader.enqueue(request) binding.coverThumbnail.alpha = if (manga.favorite) 0.34f else 1.0f } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchMangaHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchMangaHolder.kt index b1e2135119..0703a417d9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchMangaHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchMangaHolder.kt @@ -3,16 +3,18 @@ package eu.kanade.tachiyomi.ui.source.globalsearch import android.graphics.drawable.RippleDrawable import android.view.View import androidx.core.view.isVisible -import coil.Coil -import coil.dispose -import coil.request.CachePolicy -import coil.request.ImageRequest +import coil3.dispose +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.request.placeholder import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget import eu.kanade.tachiyomi.data.image.coil.MangaCoverFetcher import eu.kanade.tachiyomi.databinding.SourceGlobalSearchControllerCardItemBinding import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.util.system.dpToPx +import eu.kanade.tachiyomi.util.system.setExtras import eu.kanade.tachiyomi.util.view.makeShapeCorners import eu.kanade.tachiyomi.util.view.setCards @@ -57,9 +59,9 @@ class GlobalSearchMangaHolder(view: View, adapter: GlobalSearchCardAdapter) : .placeholder(android.R.color.transparent) .memoryCachePolicy(CachePolicy.DISABLED) .target(CoverViewTarget(binding.itemImage, binding.progress)) - .setParameter(MangaCoverFetcher.useCustomCover, false) + .setExtras(MangaCoverFetcher.USE_CUSTOM_COVER_KEY, false) .build() - Coil.imageLoader(itemView.context).enqueue(request) + itemView.context.imageLoader.enqueue(request) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaShortcutManager.kt b/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaShortcutManager.kt index 20f70d8f27..404e4a9944 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaShortcutManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/manga/MangaShortcutManager.kt @@ -7,8 +7,8 @@ import android.content.pm.ShortcutManager import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Icon -import coil.Coil -import coil.request.ImageRequest +import coil3.imageLoader +import coil3.request.ImageRequest import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.appwidget.TachiyomiWidgetManager import eu.kanade.tachiyomi.data.cache.CoverCache @@ -72,8 +72,8 @@ class MangaShortcutManager( is Manga -> { val request = ImageRequest.Builder(context).data(item).build() val bitmap = ( - Coil.imageLoader(context) - .execute(request).drawable as? BitmapDrawable + context.imageLoader + .execute(request).image?.asDrawable(context.resources) as? BitmapDrawable )?.bitmap ShortcutInfo.Builder( diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/CoilExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/CoilExtensions.kt new file mode 100644 index 0000000000..2d1fa727cd --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/CoilExtensions.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.util.system + +import coil3.Extras +import coil3.request.ImageRequest + +fun ImageRequest.Builder.setExtras(extraKey: Extras.Key, value: T): ImageRequest.Builder { + this.extras[extraKey] = value + return this +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt index cb2b24d5dd..d989fdc7b1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ImageUtil.kt @@ -93,18 +93,20 @@ object ImageUtil { } fun isAnimatedAndSupported(stream: InputStream): Boolean { - try { + return try { val type = getImageType(stream) ?: return false - return when (type.format) { + // https://coil-kt.github.io/coil/getting_started/#supported-image-formats + when (type.format) { Format.Gif -> true - // Coil supports animated WebP on Android 9.0+ - // https://coil-kt.github.io/coil/getting_started/#supported-image-formats + // Animated WebP on Android 9+ Format.Webp -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P + // Animated Heif on Android 11+ + Format.Heif -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R else -> false } } catch (_: Exception) { + false } - return false } enum class ImageType(val mime: String, val extension: String) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/GifViewTarget.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/GifViewTarget.kt index 408688c115..baedc0313b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/GifViewTarget.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/GifViewTarget.kt @@ -1,20 +1,20 @@ package eu.kanade.tachiyomi.widget -import android.graphics.drawable.Drawable import android.view.View import android.view.ViewGroup import android.widget.ImageView import androidx.core.view.isVisible -import coil.target.ImageViewTarget +import coil3.Image +import coil3.target.ImageViewTarget class GifViewTarget(view: ImageView, private val progressBar: View?, private val decodeErrorLayout: ViewGroup?) : ImageViewTarget(view) { - override fun onError(error: Drawable?) { + override fun onError(error: Image?) { progressBar?.isVisible = false decodeErrorLayout?.isVisible = true } - override fun onSuccess(result: Drawable) { + override fun onSuccess(result: Image) { progressBar?.isVisible = false decodeErrorLayout?.isVisible = false super.onSuccess(result) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e3f031f0b3..f4224f0ce8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] chucker = "3.5.2" -coil = "2.4.0" +coil3 = "3.0.0-alpha06" flexible-adapter = "c8013533" fast_adapter = "5.6.0" nucleus = "3.0.0" @@ -11,9 +11,10 @@ shizuku = "12.1.0" accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version = "0.30.1" } chucker-library-no-op = { module = "com.github.ChuckerTeam.Chucker:library-no-op", version.ref = "chucker" } chucker-library = { module = "com.github.ChuckerTeam.Chucker:library", version.ref = "chucker" } -coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" } -coil-gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" } -coil = { module = "io.coil-kt:coil", version.ref = "coil" } +coil3 = { module = "io.coil-kt.coil3:coil", version.ref = "coil3" } +coil3-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil3" } +coil3-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil3" } +coil3-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil3" } compose-theme-adapter3 = { module = "com.google.accompanist:accompanist-themeadapter-material3", version = "0.33.2-alpha" } conductor = { module = "com.bluelinelabs:conductor", version = "4.0.0-preview-4" } conductor-support-preference = { module = "com.github.tachiyomiorg:conductor-support-preference", version = "3.0.0" } @@ -77,4 +78,5 @@ kotlinter = { id = "org.jmailen.kotlinter", version = "4.1.1" } gradle-versions = { id = "com.github.ben-manes.versions", version = "0.42.0" } [bundles] +coil = [ "coil3", "coil3-svg", "coil3-gif", "coil3-okhttp" ] test = [ "junit", "mockk" ]