mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-21 20:47:03 -05:00
Switch to Coil3
Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com>
This commit is contained in:
parent
84984ef7e1
commit
f72b6e4d7c
18 changed files with 124 additions and 104 deletions
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue