Switch to Coil3

Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
This commit is contained in:
AntsyLich 2024-03-02 20:08:15 +06:00
parent 84984ef7e1
commit f72b6e4d7c
No known key found for this signature in database
18 changed files with 124 additions and 104 deletions

View File

@ -284,7 +284,7 @@ tasks {
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", "-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=com.google.accompanist.permissions.ExperimentalPermissionsApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview", "-opt-in=kotlinx.coroutines.FlowPreview",

View File

@ -25,7 +25,7 @@ import androidx.compose.ui.res.imageResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import coil.compose.AsyncImage import coil3.compose.AsyncImage
import eu.kanade.domain.source.model.icon import eu.kanade.domain.source.model.icon
import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.presentation.util.rememberResourceBitmapPainter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R

View File

@ -11,7 +11,7 @@ import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.painter.ColorPainter import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.Role
import coil.compose.AsyncImage import coil3.compose.AsyncImage
import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.presentation.util.rememberResourceBitmapPainter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R

View File

@ -37,10 +37,10 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import coil.imageLoader import coil3.imageLoader
import coil.request.CachePolicy import coil3.request.CachePolicy
import coil.request.ImageRequest import coil3.request.ImageRequest
import coil.size.Size import coil3.size.Size
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.DropdownMenu
@ -168,7 +168,9 @@ fun MangaCoverDialog(
.data(coverDataProvider()) .data(coverDataProvider())
.size(Size.ORIGINAL) .size(Size.ORIGINAL)
.memoryCachePolicy(CachePolicy.DISABLED) .memoryCachePolicy(CachePolicy.DISABLED)
.target { drawable -> .target { image ->
val drawable = image.asDrawable(view.context.resources)
// Copy bitmap in case it came from memory cache // Copy bitmap in case it came from memory cache
// Because SSIV needs to thoroughly read the image // Because SSIV needs to thoroughly read the image
val copy = (drawable as? BitmapDrawable)?.let { val copy = (drawable as? BitmapDrawable)?.let {

View File

@ -73,7 +73,7 @@ import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage import coil3.compose.AsyncImage
import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga

View File

@ -15,12 +15,14 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import coil.ImageLoader import coil3.ImageLoader
import coil.ImageLoaderFactory import coil3.SingletonImageLoader
import coil.decode.GifDecoder import coil3.disk.DiskCache
import coil.decode.ImageDecoderDecoder import coil3.disk.directory
import coil.disk.DiskCache import coil3.network.okhttp.OkHttpNetworkFetcherFactory
import coil.util.DebugLogger import coil3.request.allowRgb565
import coil3.request.crossfade
import coil3.util.DebugLogger
import eu.kanade.domain.DomainModule import eu.kanade.domain.DomainModule
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.UiPreferences
@ -58,7 +60,7 @@ import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.security.Security import java.security.Security
class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory {
private val basePreferences: BasePreferences by injectLazy() private val basePreferences: BasePreferences by injectLazy()
private val networkPreferences: NetworkPreferences by injectLazy() private val networkPreferences: NetworkPreferences by injectLazy()
@ -131,24 +133,19 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
} }
} }
override fun newImageLoader(): ImageLoader { override fun newImageLoader(context: Context): ImageLoader {
return ImageLoader.Builder(this).apply { return ImageLoader.Builder(this).apply {
val callFactoryInit = { Injekt.get<NetworkHelper>().client } val callFactoryLazy = lazy { Injekt.get<NetworkHelper>().client }
val diskCacheInit = { CoilDiskCache.get(this@App) } val diskCacheLazy = lazy { CoilDiskCache.get(this@App) }
components { components {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { add(OkHttpNetworkFetcherFactory(callFactoryLazy::value))
add(ImageDecoderDecoder.Factory())
} else {
add(GifDecoder.Factory())
}
add(TachiyomiImageDecoder.Factory()) add(TachiyomiImageDecoder.Factory())
add(MangaCoverFetcher.MangaFactory(lazy(callFactoryInit), lazy(diskCacheInit))) add(MangaCoverFetcher.MangaFactory(callFactoryLazy, diskCacheLazy))
add(MangaCoverFetcher.MangaCoverFactory(lazy(callFactoryInit), lazy(diskCacheInit))) add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy, diskCacheLazy))
add(MangaKeyer()) add(MangaKeyer())
add(MangaCoverKeyer()) add(MangaCoverKeyer())
} }
callFactory(callFactoryInit) diskCache(diskCacheLazy::value)
diskCache(diskCacheInit)
crossfade((300 * this@App.animatorDurationScale).toInt()) crossfade((300 * this@App.animatorDurationScale).toInt())
allowRgb565(DeviceUtil.isLowRamDevice(this@App)) allowRgb565(DeviceUtil.isLowRamDevice(this@App))
if (networkPreferences.verboseLogging().get()) logger(DebugLogger()) if (networkPreferences.verboseLogging().get()) logger(DebugLogger())
@ -156,7 +153,6 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
// Coil spawns a new thread for every image load by default // Coil spawns a new thread for every image load by default
fetcherDispatcher(Dispatchers.IO.limitedParallelism(8)) fetcherDispatcher(Dispatchers.IO.limitedParallelism(8))
decoderDispatcher(Dispatchers.IO.limitedParallelism(2)) decoderDispatcher(Dispatchers.IO.limitedParallelism(2))
transformationDispatcher(Dispatchers.IO.limitedParallelism(2))
}.build() }.build()
} }

View File

@ -1,19 +1,18 @@
package eu.kanade.tachiyomi.data.coil package eu.kanade.tachiyomi.data.coil
import androidx.core.net.toUri import androidx.core.net.toUri
import coil.ImageLoader import coil3.Extras
import coil.decode.DataSource import coil3.ImageLoader
import coil.decode.ImageSource import coil3.decode.DataSource
import coil.disk.DiskCache import coil3.decode.ImageSource
import coil.fetch.FetchResult import coil3.disk.DiskCache
import coil.fetch.Fetcher import coil3.fetch.FetchResult
import coil.fetch.SourceResult import coil3.fetch.Fetcher
import coil.network.HttpException import coil3.fetch.SourceFetchResult
import coil.request.Options import coil3.getOrDefault
import coil.request.Parameters import coil3.request.Options
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher.Companion.USE_CUSTOM_COVER
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import logcat.LogPriority import logcat.LogPriority
@ -22,6 +21,7 @@ import okhttp3.Call
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import okhttp3.internal.http.HTTP_NOT_MODIFIED import okhttp3.internal.http.HTTP_NOT_MODIFIED
import okio.FileSystem
import okio.Path.Companion.toOkioPath import okio.Path.Companion.toOkioPath
import okio.Source import okio.Source
import okio.buffer import okio.buffer
@ -42,7 +42,7 @@ import java.io.File
* handled by Coil's [DiskCache]. * handled by Coil's [DiskCache].
* *
* Available request parameter: * Available request parameter:
* - [USE_CUSTOM_COVER]: Use custom cover if set by user, default is true * - [USE_CUSTOM_COVER_KEY]: Use custom cover if set by user, default is true
*/ */
class MangaCoverFetcher( class MangaCoverFetcher(
private val url: String?, private val url: String?,
@ -61,7 +61,7 @@ class MangaCoverFetcher(
override suspend fun fetch(): FetchResult { override suspend fun fetch(): FetchResult {
// Use custom cover if exists // Use custom cover if exists
val useCustomCover = options.parameters.value(USE_CUSTOM_COVER) ?: true val useCustomCover = options.extras.getOrDefault(USE_CUSTOM_COVER_KEY)
if (useCustomCover) { if (useCustomCover) {
val customCoverFile = customCoverFileLazy.value val customCoverFile = customCoverFileLazy.value
if (customCoverFile.exists()) { if (customCoverFile.exists()) {
@ -80,8 +80,12 @@ class MangaCoverFetcher(
} }
private fun fileLoader(file: File): FetchResult { private fun fileLoader(file: File): FetchResult {
return SourceResult( return SourceFetchResult(
source = ImageSource(file = file.toOkioPath(), diskCacheKey = diskCacheKey), source = ImageSource(
file = file.toOkioPath(),
fileSystem = FileSystem.SYSTEM,
diskCacheKey = diskCacheKey
),
mimeType = "image/*", mimeType = "image/*",
dataSource = DataSource.DISK, dataSource = DataSource.DISK,
) )
@ -92,8 +96,8 @@ class MangaCoverFetcher(
.openInputStream() .openInputStream()
.source() .source()
.buffer() .buffer()
return SourceResult( return SourceFetchResult(
source = ImageSource(source = source, context = options.context), source = ImageSource(source = source, fileSystem = FileSystem.SYSTEM),
mimeType = "image/*", mimeType = "image/*",
dataSource = DataSource.DISK, dataSource = DataSource.DISK,
) )
@ -121,7 +125,7 @@ class MangaCoverFetcher(
} }
// Read from snapshot // Read from snapshot
return SourceResult( return SourceFetchResult(
source = snapshot.toImageSource(), source = snapshot.toImageSource(),
mimeType = "image/*", mimeType = "image/*",
dataSource = DataSource.DISK, dataSource = DataSource.DISK,
@ -141,7 +145,7 @@ class MangaCoverFetcher(
// Read from disk cache // Read from disk cache
snapshot = writeToDiskCache(response) snapshot = writeToDiskCache(response)
if (snapshot != null) { if (snapshot != null) {
return SourceResult( return SourceFetchResult(
source = snapshot.toImageSource(), source = snapshot.toImageSource(),
mimeType = "image/*", mimeType = "image/*",
dataSource = DataSource.NETWORK, dataSource = DataSource.NETWORK,
@ -149,8 +153,8 @@ class MangaCoverFetcher(
} }
// Read from response if cache is unused or unusable // Read from response if cache is unused or unusable
return SourceResult( return SourceFetchResult(
source = ImageSource(source = responseBody.source(), context = options.context), source = ImageSource(source = responseBody.source(), fileSystem = FileSystem.SYSTEM),
mimeType = "image/*", mimeType = "image/*",
dataSource = if (response.cacheResponse != null) DataSource.DISK else DataSource.NETWORK, dataSource = if (response.cacheResponse != null) DataSource.DISK else DataSource.NETWORK,
) )
@ -169,17 +173,20 @@ class MangaCoverFetcher(
val response = client.newCall(newRequest()).await() val response = client.newCall(newRequest()).await()
if (!response.isSuccessful && response.code != HTTP_NOT_MODIFIED) { if (!response.isSuccessful && response.code != HTTP_NOT_MODIFIED) {
response.close() response.close()
throw HttpException(response) throw Exception(response.message)
} }
return response return response
} }
private fun newRequest(): Request { private fun newRequest(): Request {
val request = Request.Builder() val request = Request.Builder().apply {
.url(url!!) url(url!!)
.headers(sourceLazy.value?.headers ?: options.headers)
// Support attaching custom data to the network request. val sourceHeaders = sourceLazy.value?.headers
.tag(Parameters::class.java, options.parameters) if (sourceHeaders != null) {
headers(sourceHeaders)
}
}
when { when {
options.networkCachePolicy.readEnabled -> { options.networkCachePolicy.readEnabled -> {
@ -264,7 +271,12 @@ class MangaCoverFetcher(
} }
private fun DiskCache.Snapshot.toImageSource(): ImageSource { 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 getResourceType(cover: String?): Type? { private fun getResourceType(cover: String?): Type? {
@ -330,7 +342,7 @@ class MangaCoverFetcher(
} }
companion object { companion object {
const val USE_CUSTOM_COVER = "use_custom_cover" val USE_CUSTOM_COVER_KEY = Extras.Key(true)
private val CACHE_CONTROL_NO_STORE = CacheControl.Builder().noStore().build() private val CACHE_CONTROL_NO_STORE = CacheControl.Builder().noStore().build()
private val CACHE_CONTROL_NO_NETWORK_NO_CACHE = CacheControl.Builder().noCache().onlyIfCached().build() private val CACHE_CONTROL_NO_NETWORK_NO_CACHE = CacheControl.Builder().noCache().onlyIfCached().build()

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.data.coil package eu.kanade.tachiyomi.data.coil
import coil.key.Keyer import coil3.key.Keyer
import coil.request.Options import coil3.request.Options
import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.manga.model.hasCustomCover
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.manga.model.MangaCover

View File

@ -1,13 +1,13 @@
package eu.kanade.tachiyomi.data.coil package eu.kanade.tachiyomi.data.coil
import androidx.core.graphics.drawable.toDrawable import coil3.ImageLoader
import coil.ImageLoader import coil3.asCoilImage
import coil.decode.DecodeResult import coil3.decode.DecodeResult
import coil.decode.Decoder import coil3.decode.Decoder
import coil.decode.ImageDecoderDecoder import coil3.decode.ImageSource
import coil.decode.ImageSource import coil3.fetch.SourceFetchResult
import coil.fetch.SourceResult import coil3.request.Options
import coil.request.Options import coil3.request.allowRgb565
import okio.BufferedSource import okio.BufferedSource
import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.ImageUtil
import tachiyomi.decoder.ImageDecoder import tachiyomi.decoder.ImageDecoder
@ -30,14 +30,14 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
check(bitmap != null) { "Failed to decode image" } check(bitmap != null) { "Failed to decode image" }
return DecodeResult( return DecodeResult(
drawable = bitmap.toDrawable(options.context.resources), image = bitmap.asCoilImage(),
isSampled = false, isSampled = false,
) )
} }
class Factory : Decoder.Factory { class Factory : Decoder.Factory {
override fun create(result: SourceResult, options: Options, imageLoader: ImageLoader): Decoder? { override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? {
if (!isApplicable(result.source.source())) return null if (!isApplicable(result.source.source())) return null
return TachiyomiImageDecoder(result.source, options) return TachiyomiImageDecoder(result.source, options)
} }
@ -52,7 +52,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
} }
} }
override fun equals(other: Any?) = other is ImageDecoderDecoder.Factory override fun equals(other: Any?) = other is Factory
override fun hashCode() = javaClass.hashCode() override fun hashCode() = javaClass.hashCode()
} }

View File

@ -9,9 +9,10 @@ import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import coil.imageLoader import coil3.imageLoader
import coil.request.ImageRequest import coil3.request.ImageRequest
import coil.transform.CircleCropTransformation import coil3.request.transformations
import coil3.transform.CircleCropTransformation
import eu.kanade.presentation.util.formatChapterNumber import eu.kanade.presentation.util.formatChapterNumber
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.core.security.SecurityPreferences
@ -294,7 +295,7 @@ class LibraryUpdateNotifier(
.transformations(CircleCropTransformation()) .transformations(CircleCropTransformation())
.size(NOTIF_ICON_SIZE) .size(NOTIF_ICON_SIZE)
.build() .build()
val drawable = context.imageLoader.execute(request).drawable val drawable = context.imageLoader.execute(request).image?.asDrawable(context.resources)
return drawable?.getBitmapOrNull() return drawable?.getBitmapOrNull()
} }

View File

@ -5,9 +5,9 @@ import android.net.Uri
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope import cafe.adriel.voyager.core.model.screenModelScope
import coil.imageLoader import coil3.imageLoader
import coil.request.ImageRequest import coil3.request.ImageRequest
import coil.size.Size import coil3.size.Size
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.saver.Image import eu.kanade.tachiyomi.data.saver.Image
@ -96,7 +96,7 @@ class MangaCoverScreenModel(
.build() .build()
return withIOContext { return withIOContext {
val result = context.imageLoader.execute(req).drawable val result = context.imageLoader.execute(req).image?.asDrawable(context.resources)
// TODO: Handle animated cover // TODO: Handle animated cover
val bitmap = result?.getBitmapOrNull() ?: return@withIOContext null val bitmap = result?.getBitmapOrNull() ?: return@withIOContext null

View File

@ -4,9 +4,9 @@ import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import coil.imageLoader import coil3.imageLoader
import coil.request.CachePolicy import coil3.request.CachePolicy
import coil.request.ImageRequest import coil3.request.ImageRequest
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationHandler import eu.kanade.tachiyomi.data.notification.NotificationHandler
import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.NotificationReceiver
@ -37,7 +37,7 @@ class SaveImageNotifier(private val context: Context) {
.memoryCachePolicy(CachePolicy.DISABLED) .memoryCachePolicy(CachePolicy.DISABLED)
.size(720, 1280) .size(720, 1280)
.target( .target(
onSuccess = { showCompleteNotification(uri, it.getBitmapOrNull()) }, onSuccess = { showCompleteNotification(uri, it.asDrawable(context.resources).getBitmapOrNull()) },
onError = { onError(null) }, onError = { onError(null) },
) )
.build() .build()

View File

@ -18,10 +18,11 @@ import androidx.annotation.StyleRes
import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatImageView
import androidx.core.os.postDelayed import androidx.core.os.postDelayed
import androidx.core.view.isVisible import androidx.core.view.isVisible
import coil.dispose import coil3.dispose
import coil.imageLoader import coil3.imageLoader
import coil.request.CachePolicy import coil3.request.CachePolicy
import coil.request.ImageRequest import coil3.request.ImageRequest
import coil3.request.crossfade
import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT_QUAD import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT_QUAD
@ -348,7 +349,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
.diskCachePolicy(CachePolicy.DISABLED) .diskCachePolicy(CachePolicy.DISABLED)
.target( .target(
onSuccess = { result -> onSuccess = { result ->
setImageDrawable(result) setImageDrawable(result.asDrawable(context.resources))
(result as? Animatable)?.start() (result as? Animatable)?.start()
isVisible = true isVisible = true
this@ReaderPageImageView.onImageLoaded() this@ReaderPageImageView.onImageLoaded()

View File

@ -4,7 +4,7 @@ import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import coil.drawable.ScaleDrawable import coil3.gif.ScaleDrawable
fun Drawable.getBitmapOrNull(): Bitmap? = when (this) { fun Drawable.getBitmapOrNull(): Bitmap? = when (this) {
is BitmapDrawable -> bitmap is BitmapDrawable -> bitmap

View File

@ -6,5 +6,5 @@ kotlin.mpp.androidSourceSetLayoutVersion=2
org.gradle.caching=true org.gradle.caching=true
org.gradle.configureondemand=true org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 org.gradle.jvmargs=-Xmx3g -Dfile.encoding=UTF-8
org.gradle.parallel=true org.gradle.parallel=true

View File

@ -43,10 +43,11 @@ preferencektx = "androidx.preference:preference-ktx:1.2.1"
injekt-core = "com.github.inorichi.injekt:injekt-core:65b0440" injekt-core = "com.github.inorichi.injekt:injekt-core:65b0440"
coil-bom = { module = "io.coil-kt:coil-bom", version = "2.6.0" } coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.0-alpha06" }
coil-core = { module = "io.coil-kt:coil" } coil-core = { module = "io.coil-kt.coil3:coil" }
coil-gif = { module = "io.coil-kt:coil-gif" } coil-gif = { module = "io.coil-kt.coil3:coil-gif" }
coil-compose = { module = "io.coil-kt:coil-compose" } coil-compose = { module = "io.coil-kt.coil3:coil-compose" }
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" }
subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:7e57335" subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:7e57335"
image-decoder = "com.github.tachiyomiorg:image-decoder:fbd6601290" image-decoder = "com.github.tachiyomiorg:image-decoder:fbd6601290"
@ -105,7 +106,7 @@ archive = ["common-compress", "junrar"]
okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"] okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"]
js-engine = ["quickjs-android"] js-engine = ["quickjs-android"]
sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"] sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"]
coil = ["coil-core", "coil-gif", "coil-compose"] coil = ["coil-core", "coil-gif", "coil-compose", "coil-network-okhttp"]
shizuku = ["shizuku-api", "shizuku-provider"] shizuku = ["shizuku-api", "shizuku-provider"]
sqldelight = ["sqldelight-android-driver", "sqldelight-coroutines", "sqldelight-android-paging"] sqldelight = ["sqldelight-android-driver", "sqldelight-coroutines", "sqldelight-android-paging"]
voyager = ["voyager-navigator", "voyager-screenmodel", "voyager-tab-navigator", "voyager-transitions"] voyager = ["voyager-navigator", "voyager-screenmodel", "voyager-tab-navigator", "voyager-transitions"]

View File

@ -52,7 +52,7 @@ tasks {
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
"-opt-in=coil.annotation.ExperimentalCoilApi", "-opt-in=coil3.annotation.ExperimentalCoilApi",
"-opt-in=kotlinx.coroutines.FlowPreview", "-opt-in=kotlinx.coroutines.FlowPreview",
) )
} }

View File

@ -21,13 +21,15 @@ import androidx.glance.background
import androidx.glance.layout.fillMaxSize import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.padding import androidx.glance.layout.padding
import androidx.glance.unit.ColorProvider import androidx.glance.unit.ColorProvider
import coil.executeBlocking import coil3.annotation.ExperimentalCoilApi
import coil.imageLoader import coil3.executeBlocking
import coil.request.CachePolicy import coil3.imageLoader
import coil.request.ImageRequest import coil3.request.CachePolicy
import coil.size.Precision import coil3.request.ImageRequest
import coil.size.Scale import coil3.request.transformations
import coil.transform.RoundedCornersTransformation import coil3.size.Precision
import coil3.size.Scale
import coil3.transform.RoundedCornersTransformation
import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPx
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@ -105,6 +107,7 @@ abstract class BaseUpdatesGridGlanceWidget(
} }
} }
@OptIn(ExperimentalCoilApi::class)
private suspend fun List<UpdatesWithRelations>.prepareData( private suspend fun List<UpdatesWithRelations>.prepareData(
rowCount: Int, rowCount: Int,
columnCount: Int, columnCount: Int,
@ -140,7 +143,11 @@ abstract class BaseUpdatesGridGlanceWidget(
} }
} }
.build() .build()
Pair(updatesView.mangaId, context.imageLoader.executeBlocking(request).drawable?.toBitmap()) val bitmap = context.imageLoader.executeBlocking(request)
.image
?.asDrawable(context.resources)
?.toBitmap()
Pair(updatesView.mangaId, bitmap)
} }
.toImmutableList() .toImmutableList()
} }