mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
refactor: Switch to Coil3
This commit is contained in:
parent
2bffd8a653
commit
3412806bfc
33 changed files with 286 additions and 233 deletions
|
@ -256,9 +256,7 @@ dependencies {
|
||||||
implementation(libs.injekt.core)
|
implementation(libs.injekt.core)
|
||||||
|
|
||||||
// Image library
|
// Image library
|
||||||
implementation(libs.coil)
|
implementation(libs.bundles.coil)
|
||||||
implementation(libs.coil.gif)
|
|
||||||
implementation(libs.coil.svg)
|
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation(libs.timber)
|
implementation(libs.timber)
|
||||||
|
@ -327,7 +325,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",
|
||||||
|
|
|
@ -20,6 +20,9 @@ import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.multidex.MultiDex
|
import androidx.multidex.MultiDex
|
||||||
|
import coil3.ImageLoader
|
||||||
|
import coil3.PlatformContext
|
||||||
|
import coil3.SingletonImageLoader
|
||||||
import dev.yokai.domain.AppState
|
import dev.yokai.domain.AppState
|
||||||
import eu.kanade.tachiyomi.appwidget.TachiyomiWidgetManager
|
import eu.kanade.tachiyomi.appwidget.TachiyomiWidgetManager
|
||||||
import eu.kanade.tachiyomi.data.image.coil.CoilSetup
|
import eu.kanade.tachiyomi.data.image.coil.CoilSetup
|
||||||
|
@ -44,7 +47,7 @@ import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.security.Security
|
import java.security.Security
|
||||||
|
|
||||||
open class App : Application(), DefaultLifecycleObserver {
|
open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory {
|
||||||
|
|
||||||
val preferences: PreferencesHelper by injectLazy()
|
val preferences: PreferencesHelper by injectLazy()
|
||||||
|
|
||||||
|
@ -71,7 +74,6 @@ open class App : Application(), DefaultLifecycleObserver {
|
||||||
Injekt.importModule(PreferenceModule(this))
|
Injekt.importModule(PreferenceModule(this))
|
||||||
Injekt.importModule(AppModule(this))
|
Injekt.importModule(AppModule(this))
|
||||||
|
|
||||||
CoilSetup(this)
|
|
||||||
setupNotificationChannels()
|
setupNotificationChannels()
|
||||||
|
|
||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
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"
|
private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE"
|
||||||
|
|
|
@ -16,13 +16,14 @@ import androidx.glance.appwidget.provideContent
|
||||||
import androidx.glance.appwidget.updateAll
|
import androidx.glance.appwidget.updateAll
|
||||||
import androidx.glance.background
|
import androidx.glance.background
|
||||||
import androidx.glance.layout.fillMaxSize
|
import androidx.glance.layout.fillMaxSize
|
||||||
import coil.executeBlocking
|
import coil3.executeBlocking
|
||||||
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.Precision
|
import coil3.request.transformations
|
||||||
import coil.size.Scale
|
import coil3.size.Precision
|
||||||
import coil.transform.RoundedCornersTransformation
|
import coil3.size.Scale
|
||||||
|
import coil3.transform.RoundedCornersTransformation
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.appwidget.components.CoverHeight
|
import eu.kanade.tachiyomi.appwidget.components.CoverHeight
|
||||||
import eu.kanade.tachiyomi.appwidget.components.CoverWidth
|
import eu.kanade.tachiyomi.appwidget.components.CoverWidth
|
||||||
|
@ -109,7 +110,7 @@ class UpdatesGridGlanceWidget : GlanceAppWidget() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
Pair(updatesView.id!!, app.imageLoader.executeBlocking(request).drawable?.toBitmap())
|
Pair(updatesView.id!!, app.imageLoader.executeBlocking(request).image?.asDrawable(app.resources)?.toBitmap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.data.cache
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.format.Formatter
|
import android.text.format.Formatter
|
||||||
import coil.imageLoader
|
import coil3.imageLoader
|
||||||
import coil.memory.MemoryCache
|
import coil3.memory.MemoryCache
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
|
|
@ -2,47 +2,38 @@ package eu.kanade.tachiyomi.data.image.coil
|
||||||
|
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import coil.Coil
|
import coil3.ImageLoader
|
||||||
import coil.ImageLoader
|
import coil3.disk.DiskCache
|
||||||
import coil.decode.GifDecoder
|
import coil3.disk.directory
|
||||||
import coil.decode.ImageDecoderDecoder
|
import coil3.memory.MemoryCache
|
||||||
import coil.disk.DiskCache
|
import coil3.network.okhttp.OkHttpNetworkFetcherFactory
|
||||||
import coil.memory.MemoryCache
|
import coil3.request.allowHardware
|
||||||
import coil.util.DebugLogger
|
import coil3.request.allowRgb565
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import coil3.request.crossfade
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class CoilSetup(context: Context) {
|
class CoilSetup {
|
||||||
init {
|
companion object {
|
||||||
val imageLoader = ImageLoader.Builder(context).apply {
|
fun setup(context: Context): ImageLoader {
|
||||||
val callFactoryInit = { Injekt.get<NetworkHelper>().client }
|
return ImageLoader.Builder(context).apply {
|
||||||
val diskCacheInit = { CoilDiskCache.get(context) }
|
val callFactoryLazy = lazy { Injekt.get<NetworkHelper>().client }
|
||||||
components {
|
val diskCacheLazy = lazy { CoilDiskCache.get(context) }
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
components {
|
||||||
add(ImageDecoderDecoder.Factory())
|
add(OkHttpNetworkFetcherFactory(callFactoryLazy::value))
|
||||||
} else {
|
add(TachiyomiImageDecoder.Factory())
|
||||||
add(GifDecoder.Factory())
|
add(MangaCoverFetcher.Factory(callFactoryLazy, diskCacheLazy))
|
||||||
|
add(MangaCoverKeyer())
|
||||||
}
|
}
|
||||||
add(TachiyomiImageDecoder.Factory())
|
diskCache(diskCacheLazy::value)
|
||||||
add(MangaCoverFetcher.Factory(lazy(callFactoryInit), lazy(diskCacheInit)))
|
memoryCache { MemoryCache.Builder().maxSizePercent(context, 0.40).build() }
|
||||||
add(MangaCoverKeyer())
|
crossfade(true)
|
||||||
add(InputStreamFetcher.Factory())
|
allowRgb565(context.getSystemService<ActivityManager>()!!.isLowRamDevice)
|
||||||
}
|
allowHardware(true)
|
||||||
callFactory(callFactoryInit)
|
}.build()
|
||||||
diskCache(diskCacheInit)
|
}
|
||||||
memoryCache { MemoryCache.Builder(context).maxSizePercent(0.40).build() }
|
|
||||||
crossfade(true)
|
|
||||||
allowRgb565(context.getSystemService<ActivityManager>()!!.isLowRamDevice)
|
|
||||||
allowHardware(true)
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
logger(DebugLogger())
|
|
||||||
}
|
|
||||||
}.build()
|
|
||||||
Coil.setImageLoader(imageLoader)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
|
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.R
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||||
|
|
||||||
|
@ -15,7 +16,9 @@ class CoverViewTarget(
|
||||||
val scaleType: ImageView.ScaleType = ImageView.ScaleType.CENTER_CROP,
|
val scaleType: ImageView.ScaleType = ImageView.ScaleType.CENTER_CROP,
|
||||||
) : ImageViewTarget(view) {
|
) : ImageViewTarget(view) {
|
||||||
|
|
||||||
override fun onError(error: Drawable?) {
|
override fun onError(error: Image?) {
|
||||||
|
//val drawable = error?.asDrawable(view.context.resources)
|
||||||
|
|
||||||
progress?.isVisible = false
|
progress?.isVisible = false
|
||||||
view.scaleType = ImageView.ScaleType.CENTER
|
view.scaleType = ImageView.ScaleType.CENTER
|
||||||
val vector = VectorDrawableCompat.create(
|
val vector = VectorDrawableCompat.create(
|
||||||
|
@ -27,13 +30,17 @@ class CoverViewTarget(
|
||||||
view.setImageDrawable(vector)
|
view.setImageDrawable(vector)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart(placeholder: Drawable?) {
|
override fun onStart(placeholder: Image?) {
|
||||||
|
//val drawable = placeholder?.asDrawable(view.context.resources)
|
||||||
|
|
||||||
progress?.isVisible = true
|
progress?.isVisible = true
|
||||||
view.scaleType = scaleType
|
view.scaleType = scaleType
|
||||||
super.onStart(placeholder)
|
super.onStart(placeholder)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess(result: Drawable) {
|
override fun onSuccess(result: Image) {
|
||||||
|
//val drawable = result?.asDrawable(view.context.resources)
|
||||||
|
|
||||||
progress?.isVisible = false
|
progress?.isVisible = false
|
||||||
view.scaleType = scaleType
|
view.scaleType = scaleType
|
||||||
super.onSuccess(result)
|
super.onSuccess(result)
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
package eu.kanade.tachiyomi.data.image.coil
|
package eu.kanade.tachiyomi.data.image.coil
|
||||||
|
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.palette.graphics.Palette
|
import androidx.palette.graphics.Palette
|
||||||
import coil.ImageLoader
|
import coil3.Image
|
||||||
import coil.imageLoader
|
import coil3.ImageLoader
|
||||||
import coil.memory.MemoryCache
|
import coil3.imageLoader
|
||||||
import coil.request.Disposable
|
import coil3.memory.MemoryCache
|
||||||
import coil.request.ImageRequest
|
import coil3.request.Disposable
|
||||||
import coil.target.ImageViewTarget
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.target.ImageViewTarget
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.util.system.launchIO
|
import eu.kanade.tachiyomi.util.system.launchIO
|
||||||
|
@ -22,7 +22,7 @@ class LibraryMangaImageTarget(
|
||||||
|
|
||||||
private val coverCache: CoverCache by injectLazy()
|
private val coverCache: CoverCache by injectLazy()
|
||||||
|
|
||||||
override fun onError(error: Drawable?) {
|
override fun onError(error: Image?) {
|
||||||
super.onError(error)
|
super.onError(error)
|
||||||
if (manga.favorite) {
|
if (manga.favorite) {
|
||||||
launchIO {
|
launchIO {
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
package eu.kanade.tachiyomi.data.image.coil
|
package eu.kanade.tachiyomi.data.image.coil
|
||||||
|
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
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 eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.network.await
|
import eu.kanade.tachiyomi.network.await
|
||||||
|
@ -26,17 +26,16 @@ import okhttp3.Call
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.internal.closeQuietly
|
import okhttp3.internal.closeQuietly
|
||||||
|
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
|
||||||
import okio.sink
|
import okio.sink
|
||||||
import okio.source
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.util.Date
|
import java.util.*
|
||||||
|
|
||||||
class MangaCoverFetcher(
|
class MangaCoverFetcher(
|
||||||
private val manga: Manga,
|
private val manga: Manga,
|
||||||
|
@ -71,7 +70,7 @@ class MangaCoverFetcher(
|
||||||
val networkRead = options.networkCachePolicy.readEnabled
|
val networkRead = options.networkCachePolicy.readEnabled
|
||||||
val onlyCache = !networkRead && diskRead
|
val onlyCache = !networkRead && diskRead
|
||||||
val shouldFetchRemotely = networkRead && !diskRead && !onlyCache
|
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
|
// Use custom cover if exists
|
||||||
if (!shouldFetchRemotely) {
|
if (!shouldFetchRemotely) {
|
||||||
val customCoverFile by lazy { coverCache.getCustomCoverFile(manga) }
|
val customCoverFile by lazy { coverCache.getCustomCoverFile(manga) }
|
||||||
|
@ -101,7 +100,7 @@ class MangaCoverFetcher(
|
||||||
|
|
||||||
// Read from snapshot
|
// Read from snapshot
|
||||||
setRatioAndColorsInScope(manga)
|
setRatioAndColorsInScope(manga)
|
||||||
return SourceResult(
|
return SourceFetchResult(
|
||||||
source = snapshot.toImageSource(),
|
source = snapshot.toImageSource(),
|
||||||
mimeType = "image/*",
|
mimeType = "image/*",
|
||||||
dataSource = DataSource.DISK,
|
dataSource = DataSource.DISK,
|
||||||
|
@ -122,7 +121,7 @@ class MangaCoverFetcher(
|
||||||
// Read from disk cache
|
// Read from disk cache
|
||||||
snapshot = writeToDiskCache(snapshot, response)
|
snapshot = writeToDiskCache(snapshot, 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,
|
||||||
|
@ -130,8 +129,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,
|
||||||
)
|
)
|
||||||
|
@ -149,18 +148,20 @@ class MangaCoverFetcher(
|
||||||
val client = sourceLazy.value?.client ?: callFactoryLazy.value
|
val client = sourceLazy.value?.client ?: callFactoryLazy.value
|
||||||
val response = client.newCall(newRequest()).await()
|
val response = client.newCall(newRequest()).await()
|
||||||
if (!response.isSuccessful && response.code != HttpURLConnection.HTTP_NOT_MODIFIED) {
|
if (!response.isSuccessful && response.code != HttpURLConnection.HTTP_NOT_MODIFIED) {
|
||||||
response.body?.closeQuietly()
|
response.body.closeQuietly()
|
||||||
throw HttpException(response)
|
throw Exception(response.message) // FIXME: Should probably use something else other than generic Exception
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
}
|
||||||
|
|
||||||
val diskRead = options.diskCachePolicy.readEnabled
|
val diskRead = options.diskCachePolicy.readEnabled
|
||||||
val networkRead = options.networkCachePolicy.readEnabled
|
val networkRead = options.networkCachePolicy.readEnabled
|
||||||
|
@ -227,7 +228,11 @@ class MangaCoverFetcher(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readFromDiskCache(): DiskCache.Snapshot? {
|
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(
|
private fun writeToDiskCache(
|
||||||
|
@ -239,15 +244,15 @@ class MangaCoverFetcher(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val editor = if (snapshot != null) {
|
val editor = if (snapshot != null) {
|
||||||
snapshot.closeAndEdit()
|
snapshot.closeAndOpenEditor()
|
||||||
} else {
|
} else {
|
||||||
diskCacheLazy.value.edit(diskCacheKey!!)
|
diskCacheLazy.value.openEditor(diskCacheKey!!)
|
||||||
} ?: return null
|
} ?: return null
|
||||||
try {
|
try {
|
||||||
diskCacheLazy.value.fileSystem.write(editor.data) {
|
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) {
|
} catch (e: Exception) {
|
||||||
try {
|
try {
|
||||||
editor.abort()
|
editor.abort()
|
||||||
|
@ -258,7 +263,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 setRatioAndColorsInScope(manga: Manga, ogFile: File? = null, force: Boolean = false) {
|
private fun setRatioAndColorsInScope(manga: Manga, ogFile: File? = null, force: Boolean = false) {
|
||||||
|
@ -283,8 +293,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,
|
||||||
)
|
)
|
||||||
|
@ -318,7 +332,7 @@ class MangaCoverFetcher(
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
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_FORCE_NETWORK_NO_CACHE = CacheControl.Builder().noCache().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.image.coil
|
package eu.kanade.tachiyomi.data.image.coil
|
||||||
|
|
||||||
import coil.key.Keyer
|
import coil3.key.Keyer
|
||||||
import coil.request.Options
|
import coil3.request.Options
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,15 @@ package eu.kanade.tachiyomi.data.image.coil
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
import coil3.ImageLoader
|
||||||
import coil.ImageLoader
|
import coil3.asCoilImage
|
||||||
import coil.decode.*
|
import coil3.decode.DecodeResult
|
||||||
import coil.fetch.SourceResult
|
import coil3.decode.DecodeUtils
|
||||||
import coil.request.Options
|
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.GLUtil
|
||||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||||
import okio.BufferedSource
|
import okio.BufferedSource
|
||||||
|
@ -79,16 +83,16 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return DecodeResult(
|
return DecodeResult(
|
||||||
drawable = bitmap.toDrawable(options.context.resources),
|
image = bitmap.asCoilImage(),
|
||||||
isSampled = sampleSize > 1,
|
isSampled = sampleSize > 1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
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()) || options.customDecoder) return TachiyomiImageDecoder(result.source, options)
|
if (!isApplicable(result.source.source())) return null
|
||||||
return null
|
return TachiyomiImageDecoder(result.source, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isApplicable(source: BufferedSource): Boolean {
|
private fun isApplicable(source: BufferedSource): Boolean {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package eu.kanade.tachiyomi.data.image.coil
|
package eu.kanade.tachiyomi.data.image.coil
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import coil3.Extras
|
||||||
import android.os.Build
|
import coil3.getExtra
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import coil.request.Options
|
import coil3.request.Options
|
||||||
import coil.size.Dimension
|
import coil3.size.Dimension
|
||||||
import coil.size.Scale
|
import coil3.size.Scale
|
||||||
import coil.size.Size
|
import coil3.size.Size
|
||||||
import coil.size.isOriginal
|
import coil3.size.isOriginal
|
||||||
import coil.size.pxOrElse
|
import coil3.size.pxOrElse
|
||||||
|
|
||||||
internal inline fun Size.widthPx(scale: Scale, original: () -> Int): Int {
|
internal inline fun Size.widthPx(scale: Scale, original: () -> Int): Int {
|
||||||
return if (isOriginal) original() else width.toPx(scale)
|
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 {
|
fun ImageRequest.Builder.cropBorders(enable: Boolean) = apply {
|
||||||
setParameter(cropBordersKey, enable)
|
extras[cropBordersKey] = enable
|
||||||
}
|
}
|
||||||
|
|
||||||
val Options.cropBorders: Boolean
|
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 {
|
fun ImageRequest.Builder.customDecoder(enable: Boolean) = apply {
|
||||||
setParameter(customDecoderKey, enable)
|
extras[customDecoderKey] = enable
|
||||||
}
|
}
|
||||||
|
|
||||||
val Options.customDecoder: Boolean
|
val Options.customDecoder: Boolean
|
||||||
get() = parameters.value(customDecoderKey) ?: false
|
get() = getExtra(customDecoderKey)
|
||||||
|
|
||||||
private val customDecoderKey = "custom_decoder"
|
private val customDecoderKey = Extras.Key(default = false)
|
||||||
|
|
||||||
val Options.bitmapConfig: Bitmap.Config
|
|
||||||
get() = this.config
|
|
||||||
|
|
|
@ -16,9 +16,9 @@ import androidx.work.WorkInfo
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import androidx.work.WorkQuery
|
import androidx.work.WorkQuery
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import coil.Coil
|
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.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
|
@ -261,21 +261,19 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
val thumbnailUrl = manga.thumbnail_url
|
val thumbnailUrl = manga.thumbnail_url
|
||||||
manga.copyFrom(networkManga)
|
manga.copyFrom(networkManga)
|
||||||
manga.initialized = true
|
manga.initialized = true
|
||||||
if (thumbnailUrl != manga.thumbnail_url) {
|
val request: ImageRequest =
|
||||||
coverCache.deleteFromCache(thumbnailUrl)
|
if (thumbnailUrl != manga.thumbnail_url) {
|
||||||
// load new covers in background
|
coverCache.deleteFromCache(thumbnailUrl)
|
||||||
val request =
|
// load new covers in background
|
||||||
ImageRequest.Builder(context).data(manga)
|
ImageRequest.Builder(context).data(manga)
|
||||||
.memoryCachePolicy(CachePolicy.DISABLED).build()
|
.memoryCachePolicy(CachePolicy.DISABLED).build()
|
||||||
Coil.imageLoader(context).execute(request)
|
} else {
|
||||||
} else {
|
|
||||||
val request =
|
|
||||||
ImageRequest.Builder(context).data(manga)
|
ImageRequest.Builder(context).data(manga)
|
||||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||||
.diskCachePolicy(CachePolicy.WRITE_ONLY)
|
.diskCachePolicy(CachePolicy.WRITE_ONLY)
|
||||||
.build()
|
.build()
|
||||||
Coil.imageLoader(context).execute(request)
|
}
|
||||||
}
|
context.imageLoader.execute(request)
|
||||||
db.insertManga(manga).executeAsBlocking()
|
db.insertManga(manga).executeAsBlocking()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,11 @@ import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import coil.Coil
|
import coil3.imageLoader
|
||||||
import coil.request.CachePolicy
|
import coil3.request.CachePolicy
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import coil.transform.CircleCropTransformation
|
import coil3.request.transformations
|
||||||
|
import coil3.transform.CircleCropTransformation
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
import eu.kanade.tachiyomi.data.database.models.LibraryManga
|
||||||
|
@ -191,11 +192,11 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
.transformations(CircleCropTransformation())
|
.transformations(CircleCropTransformation())
|
||||||
.size(width = ICON_SIZE, height = ICON_SIZE).build()
|
.size(width = ICON_SIZE, height = ICON_SIZE).build()
|
||||||
|
|
||||||
Coil.imageLoader(context)
|
context.imageLoader
|
||||||
.execute(request).drawable?.let { drawable ->
|
.execute(request).image?.asDrawable(context.resources)?.let { drawable ->
|
||||||
setLargeIcon((drawable as? BitmapDrawable)?.bitmap)
|
setLargeIcon((drawable as? BitmapDrawable)?.bitmap)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
}
|
}
|
||||||
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||||
setContentTitle(manga.title)
|
setContentTitle(manga.title)
|
||||||
|
|
|
@ -9,8 +9,8 @@ import androidx.core.text.color
|
||||||
import androidx.core.text.scale
|
import androidx.core.text.scale
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import coil.dispose
|
import coil3.dispose
|
||||||
import coil.load
|
import coil3.load
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget
|
import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget
|
||||||
import eu.kanade.tachiyomi.databinding.ExtensionCardItemBinding
|
import eu.kanade.tachiyomi.databinding.ExtensionCardItemBinding
|
||||||
|
|
|
@ -10,9 +10,9 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.marginBottom
|
import androidx.core.view.marginBottom
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import coil.dispose
|
import coil3.dispose
|
||||||
import coil.size.Precision
|
import coil3.size.Precision
|
||||||
import coil.size.Scale
|
import coil3.size.Scale
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.image.coil.loadManga
|
import eu.kanade.tachiyomi.data.image.coil.loadManga
|
||||||
|
|
|
@ -4,7 +4,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import coil.dispose
|
import coil3.dispose
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.image.coil.loadManga
|
import eu.kanade.tachiyomi.data.image.coil.loadManga
|
||||||
import eu.kanade.tachiyomi.databinding.MangaListItemBinding
|
import eu.kanade.tachiyomi.databinding.MangaListItemBinding
|
||||||
|
|
|
@ -13,8 +13,7 @@ import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import coil.load
|
import coil3.load
|
||||||
import coil.request.Parameters
|
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import com.google.android.material.chip.ChipGroup
|
import com.google.android.material.chip.ChipGroup
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
@ -215,10 +214,9 @@ class EditMangaDialog : DialogController {
|
||||||
binding.resetCover.setOnClickListener {
|
binding.resetCover.setOnClickListener {
|
||||||
binding.mangaCover.load(
|
binding.mangaCover.load(
|
||||||
manga,
|
manga,
|
||||||
builder = {
|
) {
|
||||||
parameters(Parameters.Builder().set(MangaCoverFetcher.useCustomCover, false).build())
|
extras[MangaCoverFetcher.USE_CUSTOM_COVER_KEY] = false
|
||||||
},
|
}
|
||||||
)
|
|
||||||
customCoverUri = null
|
customCoverUri = null
|
||||||
willResetCover = true
|
willResetCover = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.app.PendingIntent
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
|
@ -40,8 +41,9 @@ import androidx.palette.graphics.Palette
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.imageLoader
|
import coil3.imageLoader
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.allowHardware
|
||||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||||
import com.bluelinelabs.conductor.ControllerChangeType
|
import com.bluelinelabs.conductor.ControllerChangeType
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
|
@ -569,8 +571,20 @@ class MangaDetailsController :
|
||||||
val request = ImageRequest.Builder(view.context).data(presenter.manga).allowHardware(false)
|
val request = ImageRequest.Builder(view.context).data(presenter.manga).allowHardware(false)
|
||||||
.memoryCacheKey(presenter.manga.key())
|
.memoryCacheKey(presenter.manga.key())
|
||||||
.target(
|
.target(
|
||||||
onSuccess = { drawable ->
|
onSuccess = { image ->
|
||||||
val bitmap = (drawable as? BitmapDrawable)?.bitmap
|
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.
|
// Generate the Palette on a background thread.
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
Palette.from(bitmap).generate {
|
Palette.from(bitmap).generate {
|
||||||
|
@ -590,7 +604,7 @@ class MangaDetailsController :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.mangaCoverFull.setImageDrawable(drawable)
|
binding.mangaCoverFull.setImageDrawable(copy)
|
||||||
getHeader()?.updateCover(manga!!)
|
getHeader()?.updateCover(manga!!)
|
||||||
},
|
},
|
||||||
onError = {
|
onError = {
|
||||||
|
|
|
@ -4,12 +4,11 @@ import android.app.Application
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import coil.Coil
|
import coil3.imageLoader
|
||||||
import coil.imageLoader
|
import coil3.memory.MemoryCache
|
||||||
import coil.memory.MemoryCache
|
import coil3.request.CachePolicy
|
||||||
import coil.request.CachePolicy
|
import coil3.request.ImageRequest
|
||||||
import coil.request.ImageRequest
|
import coil3.request.SuccessResult
|
||||||
import coil.request.SuccessResult
|
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
|
@ -379,7 +378,7 @@ class MangaDetailsPresenter(
|
||||||
.diskCachePolicy(CachePolicy.WRITE_ONLY)
|
.diskCachePolicy(CachePolicy.WRITE_ONLY)
|
||||||
.build()
|
.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()))
|
preferences.context.imageLoader.memoryCache?.remove(MemoryCache.Key(manga.key()))
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
view?.setPaletteColor()
|
view?.setPaletteColor()
|
||||||
|
|
|
@ -25,7 +25,9 @@ import androidx.core.view.updateLayoutParams
|
||||||
import androidx.core.widget.TextViewCompat
|
import androidx.core.widget.TextViewCompat
|
||||||
import androidx.transition.TransitionSet
|
import androidx.transition.TransitionSet
|
||||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
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.button.MaterialButton
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
@ -289,7 +291,7 @@ class MangaHeaderHolder(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n", "StringFormatInvalid")
|
||||||
fun bind(item: MangaHeaderItem, manga: Manga) {
|
fun bind(item: MangaHeaderItem, manga: Manga) {
|
||||||
val presenter = adapter.delegate.mangaPresenter()
|
val presenter = adapter.delegate.mangaPresenter()
|
||||||
if (binding == null) {
|
if (binding == null) {
|
||||||
|
@ -680,9 +682,10 @@ class MangaHeaderHolder(
|
||||||
diskCachePolicy(CachePolicy.READ_ONLY)
|
diskCachePolicy(CachePolicy.READ_ONLY)
|
||||||
target(
|
target(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
val bitmap = (it as? BitmapDrawable)?.bitmap
|
val result = it.asDrawable(itemView.resources)
|
||||||
|
val bitmap = (result as? BitmapDrawable)?.bitmap
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
binding.backdrop.setImageDrawable(it)
|
binding.backdrop.setImageDrawable(result)
|
||||||
return@target
|
return@target
|
||||||
}
|
}
|
||||||
val yOffset = (bitmap.height / 2 * 0.33).toInt()
|
val yOffset = (bitmap.height / 2 * 0.33).toInt()
|
||||||
|
|
|
@ -2,8 +2,9 @@ package eu.kanade.tachiyomi.ui.manga.track
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import coil.dispose
|
import coil3.dispose
|
||||||
import coil.load
|
import coil3.load
|
||||||
|
import coil3.request.allowHardware
|
||||||
import com.google.android.material.shape.CornerFamily
|
import com.google.android.material.shape.CornerFamily
|
||||||
import com.mikepenz.fastadapter.FastAdapter
|
import com.mikepenz.fastadapter.FastAdapter
|
||||||
import com.mikepenz.fastadapter.items.AbstractItem
|
import com.mikepenz.fastadapter.items.AbstractItem
|
||||||
|
|
|
@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.migration
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.dispose
|
import coil3.dispose
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
import eu.kanade.tachiyomi.data.image.coil.loadManga
|
import eu.kanade.tachiyomi.data.image.coil.loadManga
|
||||||
|
|
|
@ -5,8 +5,8 @@ import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.view.isInvisible
|
import androidx.core.view.isInvisible
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import coil.Coil
|
import coil3.imageLoader
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
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.library.setFreeformCoverRatio
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
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.setCards
|
||||||
import eu.kanade.tachiyomi.util.view.setVectorCompat
|
import eu.kanade.tachiyomi.util.view.setVectorCompat
|
||||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||||
|
@ -147,9 +148,9 @@ class MigrationProcessHolder(
|
||||||
|
|
||||||
val request = ImageRequest.Builder(view.context).data(manga)
|
val request = ImageRequest.Builder(view.context).data(manga)
|
||||||
.target(CoverViewTarget(coverThumbnail, progress))
|
.target(CoverViewTarget(coverThumbnail, progress))
|
||||||
.setParameter(MangaCoverFetcher.useCustomCover, false)
|
.setExtras(MangaCoverFetcher.USE_CUSTOM_COVER_KEY, false)
|
||||||
.build()
|
.build()
|
||||||
Coil.imageLoader(view.context).enqueue(request)
|
view.context.imageLoader.enqueue(request)
|
||||||
|
|
||||||
compactTitle.isVisible = true
|
compactTitle.isVisible = true
|
||||||
gradient.isVisible = true
|
gradient.isVisible = true
|
||||||
|
|
|
@ -5,9 +5,9 @@ import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import coil.Coil
|
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
|
||||||
|
@ -51,7 +51,7 @@ class SaveImageNotifier(private val context: Context) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
).build()
|
).build()
|
||||||
Coil.imageLoader(context).enqueue(request)
|
context.imageLoader.enqueue(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showCompleteNotification(file: File, image: Bitmap) {
|
private fun showCompleteNotification(file: File, image: Bitmap) {
|
||||||
|
|
|
@ -18,12 +18,14 @@ import androidx.annotation.CallSuper
|
||||||
import androidx.annotation.StyleRes
|
import androidx.annotation.StyleRes
|
||||||
import androidx.appcompat.widget.AppCompatImageView
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import coil.dispose
|
import coil3.BitmapImage
|
||||||
import coil.imageLoader
|
import coil3.dispose
|
||||||
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.ViewSizeResolver
|
import coil3.request.crossfade
|
||||||
|
import coil3.size.Precision
|
||||||
|
import coil3.size.ViewSizeResolver
|
||||||
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.SCALE_TYPE_CENTER_INSIDE
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE
|
||||||
|
@ -180,7 +182,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setNonAnimatedImage(
|
private fun setNonAnimatedImage(
|
||||||
image: Any,
|
data: Any,
|
||||||
config: Config,
|
config: Config,
|
||||||
) = (pageView as? SubsamplingScaleImageView)?.apply {
|
) = (pageView as? SubsamplingScaleImageView)?.apply {
|
||||||
setDoubleTapZoomDuration(config.zoomDuration.getSystemScaledDuration())
|
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"
|
val useCoilPipeline = false // FIXME: "Bitmap too large to be uploaded into a texture"
|
||||||
if (isWebtoon && useCoilPipeline) {
|
if (isWebtoon && useCoilPipeline) {
|
||||||
val request = ImageRequest.Builder(context)
|
val request = ImageRequest.Builder(context)
|
||||||
.data(image)
|
.data(data)
|
||||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||||
.diskCachePolicy(CachePolicy.DISABLED)
|
.diskCachePolicy(CachePolicy.DISABLED)
|
||||||
.target(
|
.target(
|
||||||
onSuccess = { result ->
|
onSuccess = { result ->
|
||||||
val drawable = result as BitmapDrawable
|
val image = result as BitmapDrawable
|
||||||
setImage(ImageSource.bitmap(drawable.bitmap))
|
setImage(ImageSource.bitmap(image.bitmap))
|
||||||
isVisible = true
|
isVisible = true
|
||||||
},
|
},
|
||||||
onError = {
|
onError = {
|
||||||
|
@ -247,10 +249,10 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
||||||
.build()
|
.build()
|
||||||
context.imageLoader.enqueue(request)
|
context.imageLoader.enqueue(request)
|
||||||
} else {
|
} else {
|
||||||
when (image) {
|
when (data) {
|
||||||
is BitmapDrawable -> setImage(ImageSource.bitmap(image.bitmap))
|
is BitmapDrawable -> setImage(ImageSource.bitmap(data.bitmap))
|
||||||
is InputStream -> setImage(ImageSource.inputStream(image))
|
is InputStream -> setImage(ImageSource.inputStream(data))
|
||||||
else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}")
|
else -> throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}")
|
||||||
}
|
}
|
||||||
isVisible = true
|
isVisible = true
|
||||||
}
|
}
|
||||||
|
@ -314,8 +316,9 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
||||||
.diskCachePolicy(CachePolicy.DISABLED)
|
.diskCachePolicy(CachePolicy.DISABLED)
|
||||||
.target(
|
.target(
|
||||||
onSuccess = { result ->
|
onSuccess = { result ->
|
||||||
setImageDrawable(result)
|
val drawable = result.asDrawable(context.resources)
|
||||||
(result as? Animatable)?.start()
|
setImageDrawable(drawable)
|
||||||
|
(drawable as? Animatable)?.start()
|
||||||
isVisible = true
|
isVisible = true
|
||||||
this@ReaderPageImageView.onImageLoaded()
|
this@ReaderPageImageView.onImageLoaded()
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,9 +5,9 @@ import android.view.View
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.Coil
|
import coil3.dispose
|
||||||
import coil.dispose
|
import coil3.imageLoader
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
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.data.image.coil.MangaCoverFetcher
|
||||||
import eu.kanade.tachiyomi.databinding.MangaGridItemBinding
|
import eu.kanade.tachiyomi.databinding.MangaGridItemBinding
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryCategoryAdapter
|
import eu.kanade.tachiyomi.ui.library.LibraryCategoryAdapter
|
||||||
|
import eu.kanade.tachiyomi.util.system.setExtras
|
||||||
import eu.kanade.tachiyomi.util.view.setCards
|
import eu.kanade.tachiyomi.util.view.setCards
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,9 +68,9 @@ class BrowseSourceGridHolder(
|
||||||
manga.id ?: return
|
manga.id ?: return
|
||||||
val request = ImageRequest.Builder(view.context).data(manga)
|
val request = ImageRequest.Builder(view.context).data(manga)
|
||||||
.target(CoverViewTarget(binding.coverThumbnail, binding.progress))
|
.target(CoverViewTarget(binding.coverThumbnail, binding.progress))
|
||||||
.setParameter(MangaCoverFetcher.useCustomCover, false)
|
.setExtras(MangaCoverFetcher.USE_CUSTOM_COVER_KEY, false)
|
||||||
.build()
|
.build()
|
||||||
Coil.imageLoader(view.context).enqueue(request)
|
view.context.imageLoader.enqueue(request)
|
||||||
|
|
||||||
binding.coverThumbnail.alpha = if (manga.favorite) 0.34f else 1.0f
|
binding.coverThumbnail.alpha = if (manga.favorite) 0.34f else 1.0f
|
||||||
binding.card.strokeColorStateList?.defaultColor?.let { color ->
|
binding.card.strokeColorStateList?.defaultColor?.let { color ->
|
||||||
|
|
|
@ -3,15 +3,16 @@ package eu.kanade.tachiyomi.ui.source.browse
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.Coil
|
import coil3.dispose
|
||||||
import coil.dispose
|
import coil3.imageLoader
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget
|
import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget
|
||||||
import eu.kanade.tachiyomi.data.image.coil.MangaCoverFetcher
|
import eu.kanade.tachiyomi.data.image.coil.MangaCoverFetcher
|
||||||
import eu.kanade.tachiyomi.databinding.MangaListItemBinding
|
import eu.kanade.tachiyomi.databinding.MangaListItemBinding
|
||||||
|
import eu.kanade.tachiyomi.util.system.setExtras
|
||||||
import eu.kanade.tachiyomi.util.view.setCards
|
import eu.kanade.tachiyomi.util.view.setCards
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,9 +57,9 @@ class BrowseSourceListHolder(
|
||||||
manga.id ?: return
|
manga.id ?: return
|
||||||
val request = ImageRequest.Builder(view.context).data(manga)
|
val request = ImageRequest.Builder(view.context).data(manga)
|
||||||
.target(CoverViewTarget(binding.coverThumbnail))
|
.target(CoverViewTarget(binding.coverThumbnail))
|
||||||
.setParameter(MangaCoverFetcher.useCustomCover, false)
|
.setExtras(MangaCoverFetcher.USE_CUSTOM_COVER_KEY, false)
|
||||||
.build()
|
.build()
|
||||||
Coil.imageLoader(view.context).enqueue(request)
|
view.context.imageLoader.enqueue(request)
|
||||||
|
|
||||||
binding.coverThumbnail.alpha = if (manga.favorite) 0.34f else 1.0f
|
binding.coverThumbnail.alpha = if (manga.favorite) 0.34f else 1.0f
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,18 @@ package eu.kanade.tachiyomi.ui.source.globalsearch
|
||||||
import android.graphics.drawable.RippleDrawable
|
import android.graphics.drawable.RippleDrawable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import coil.Coil
|
import coil3.dispose
|
||||||
import coil.dispose
|
import coil3.imageLoader
|
||||||
import coil.request.CachePolicy
|
import coil3.request.CachePolicy
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.placeholder
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget
|
import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget
|
||||||
import eu.kanade.tachiyomi.data.image.coil.MangaCoverFetcher
|
import eu.kanade.tachiyomi.data.image.coil.MangaCoverFetcher
|
||||||
import eu.kanade.tachiyomi.databinding.SourceGlobalSearchControllerCardItemBinding
|
import eu.kanade.tachiyomi.databinding.SourceGlobalSearchControllerCardItemBinding
|
||||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
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.makeShapeCorners
|
||||||
import eu.kanade.tachiyomi.util.view.setCards
|
import eu.kanade.tachiyomi.util.view.setCards
|
||||||
|
|
||||||
|
@ -57,9 +59,9 @@ class GlobalSearchMangaHolder(view: View, adapter: GlobalSearchCardAdapter) :
|
||||||
.placeholder(android.R.color.transparent)
|
.placeholder(android.R.color.transparent)
|
||||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||||
.target(CoverViewTarget(binding.itemImage, binding.progress))
|
.target(CoverViewTarget(binding.itemImage, binding.progress))
|
||||||
.setParameter(MangaCoverFetcher.useCustomCover, false)
|
.setExtras(MangaCoverFetcher.USE_CUSTOM_COVER_KEY, false)
|
||||||
.build()
|
.build()
|
||||||
Coil.imageLoader(itemView.context).enqueue(request)
|
itemView.context.imageLoader.enqueue(request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import android.content.pm.ShortcutManager
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import coil.Coil
|
import coil3.imageLoader
|
||||||
import coil.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.appwidget.TachiyomiWidgetManager
|
import eu.kanade.tachiyomi.appwidget.TachiyomiWidgetManager
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
|
@ -72,8 +72,8 @@ class MangaShortcutManager(
|
||||||
is Manga -> {
|
is Manga -> {
|
||||||
val request = ImageRequest.Builder(context).data(item).build()
|
val request = ImageRequest.Builder(context).data(item).build()
|
||||||
val bitmap = (
|
val bitmap = (
|
||||||
Coil.imageLoader(context)
|
context.imageLoader
|
||||||
.execute(request).drawable as? BitmapDrawable
|
.execute(request).image?.asDrawable(context.resources) as? BitmapDrawable
|
||||||
)?.bitmap
|
)?.bitmap
|
||||||
|
|
||||||
ShortcutInfo.Builder(
|
ShortcutInfo.Builder(
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package eu.kanade.tachiyomi.util.system
|
||||||
|
|
||||||
|
import coil3.Extras
|
||||||
|
import coil3.request.ImageRequest
|
||||||
|
|
||||||
|
fun <T> ImageRequest.Builder.setExtras(extraKey: Extras.Key<T>, value: T): ImageRequest.Builder {
|
||||||
|
this.extras[extraKey] = value
|
||||||
|
return this
|
||||||
|
}
|
|
@ -93,18 +93,20 @@ object ImageUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isAnimatedAndSupported(stream: InputStream): Boolean {
|
fun isAnimatedAndSupported(stream: InputStream): Boolean {
|
||||||
try {
|
return try {
|
||||||
val type = getImageType(stream) ?: return false
|
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
|
Format.Gif -> true
|
||||||
// Coil supports animated WebP on Android 9.0+
|
// Animated WebP on Android 9+
|
||||||
// https://coil-kt.github.io/coil/getting_started/#supported-image-formats
|
|
||||||
Format.Webp -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
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
|
else -> false
|
||||||
}
|
}
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ImageType(val mime: String, val extension: String) {
|
enum class ImageType(val mime: String, val extension: String) {
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
package eu.kanade.tachiyomi.widget
|
package eu.kanade.tachiyomi.widget
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.core.view.isVisible
|
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) {
|
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
|
progressBar?.isVisible = false
|
||||||
decodeErrorLayout?.isVisible = true
|
decodeErrorLayout?.isVisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess(result: Drawable) {
|
override fun onSuccess(result: Image) {
|
||||||
progressBar?.isVisible = false
|
progressBar?.isVisible = false
|
||||||
decodeErrorLayout?.isVisible = false
|
decodeErrorLayout?.isVisible = false
|
||||||
super.onSuccess(result)
|
super.onSuccess(result)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[versions]
|
[versions]
|
||||||
chucker = "3.5.2"
|
chucker = "3.5.2"
|
||||||
coil = "2.4.0"
|
coil3 = "3.0.0-alpha06"
|
||||||
flexible-adapter = "c8013533"
|
flexible-adapter = "c8013533"
|
||||||
fast_adapter = "5.6.0"
|
fast_adapter = "5.6.0"
|
||||||
nucleus = "3.0.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" }
|
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-no-op = { module = "com.github.ChuckerTeam.Chucker:library-no-op", version.ref = "chucker" }
|
||||||
chucker-library = { module = "com.github.ChuckerTeam.Chucker:library", 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" }
|
coil3 = { module = "io.coil-kt.coil3:coil", version.ref = "coil3" }
|
||||||
coil-gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" }
|
coil3-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil3" }
|
||||||
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
|
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" }
|
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 = { module = "com.bluelinelabs:conductor", version = "4.0.0-preview-4" }
|
||||||
conductor-support-preference = { module = "com.github.tachiyomiorg:conductor-support-preference", version = "3.0.0" }
|
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" }
|
gradle-versions = { id = "com.github.ben-manes.versions", version = "0.42.0" }
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
|
coil = [ "coil3", "coil3-svg", "coil3-gif", "coil3-okhttp" ]
|
||||||
test = [ "junit", "mockk" ]
|
test = [ "junit", "mockk" ]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue