Upgrade coil to v2

This commit is contained in:
2022-05-04 13:17:02 +03:00
parent 4c5314fe59
commit ffad6a4ae6
12 changed files with 73 additions and 53 deletions

View File

@@ -52,6 +52,7 @@ android {
'-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi', '-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi',
'-opt-in=kotlinx.coroutines.FlowPreview', '-opt-in=kotlinx.coroutines.FlowPreview',
'-opt-in=kotlin.contracts.ExperimentalContracts', '-opt-in=kotlin.contracts.ExperimentalContracts',
'-opt-in=coil.annotation.ExperimentalCoilApi',
] ]
} }
lint { lint {
@@ -86,7 +87,7 @@ dependencies {
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01' implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.work:work-runtime-ktx:2.7.1' implementation 'androidx.work:work-runtime-ktx:2.7.1'
implementation 'com.google.android.material:material:1.6.0-rc01' implementation 'com.google.android.material:material:1.7.0-alpha01'
//noinspection LifecycleAnnotationProcessorWithJava8 //noinspection LifecycleAnnotationProcessorWithJava8
kapt 'androidx.lifecycle:lifecycle-compiler:2.4.1' kapt 'androidx.lifecycle:lifecycle-compiler:2.4.1'
@@ -101,11 +102,11 @@ dependencies {
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2' implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2'
implementation 'io.insert-koin:koin-android:3.1.6' implementation 'io.insert-koin:koin-android:3.1.6'
implementation 'io.coil-kt:coil-base:1.4.0' implementation 'io.coil-kt:coil-base:2.0.0-rc03'
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0' implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
implementation 'com.github.solkin:disk-lru-cache:1.4' implementation 'com.github.solkin:disk-lru-cache:1.4'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1'

View File

@@ -5,12 +5,12 @@ import android.content.Context
import android.content.pm.ShortcutManager import android.content.pm.ShortcutManager
import android.media.ThumbnailUtils import android.media.ThumbnailUtils
import android.os.Build import android.os.Build
import android.util.Size
import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import coil.ImageLoader import coil.ImageLoader
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.size.PixelSize
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
@@ -54,7 +54,7 @@ class ShortcutsRepository(
val bmp = coil.execute( val bmp = coil.execute(
ImageRequest.Builder(context) ImageRequest.Builder(context)
.data(manga.coverUrl) .data(manga.coverUrl)
.size(iconSize) .size(iconSize.width, iconSize.height)
.build() .build()
).requireBitmap() ).requireBitmap()
ThumbnailUtils.extractThumbnail(bmp, iconSize.width, iconSize.height, 0) ThumbnailUtils.extractThumbnail(bmp, iconSize.width, iconSize.height, 0)
@@ -74,14 +74,14 @@ class ShortcutsRepository(
) )
} }
private fun getIconSize(context: Context): PixelSize { private fun getIconSize(context: Context): Size {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
(context.getSystemService(Context.SHORTCUT_SERVICE) as ShortcutManager).let { (context.getSystemService(Context.SHORTCUT_SERVICE) as ShortcutManager).let {
PixelSize(it.iconMaxWidth, it.iconMaxHeight) Size(it.iconMaxWidth, it.iconMaxHeight)
} }
} else { } else {
(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).launcherLargeIconSize.let { (context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).launcherLargeIconSize.let {
PixelSize(it, it) Size(it, it)
} }
} }
} }

View File

@@ -2,17 +2,19 @@ package org.koitharu.kotatsu.core.parser
import android.net.Uri import android.net.Uri
import coil.map.Mapper import coil.map.Mapper
import coil.request.Options
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
class FaviconMapper() : Mapper<Uri, HttpUrl> { class FaviconMapper : Mapper<Uri, HttpUrl> {
override fun map(data: Uri): HttpUrl { override fun map(data: Uri, options: Options): HttpUrl? {
if (data.scheme != "favicon") {
return null
}
val mangaSource = MangaSource.valueOf(data.schemeSpecificPart) val mangaSource = MangaSource.valueOf(data.schemeSpecificPart)
val repo = MangaRepository(mangaSource) as RemoteMangaRepository val repo = MangaRepository(mangaSource) as RemoteMangaRepository
return repo.getFaviconUrl().toHttpUrl() return repo.getFaviconUrl().toHttpUrl()
} }
override fun handles(data: Uri) = data.scheme == "favicon"
} }

View File

@@ -2,11 +2,13 @@ package org.koitharu.kotatsu.core.ui
import coil.ComponentRegistry import coil.ComponentRegistry
import coil.ImageLoader import coil.ImageLoader
import coil.util.CoilUtils import coil.disk.DiskCache
import kotlinx.coroutines.Dispatchers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module import org.koin.dsl.module
import org.koitharu.kotatsu.core.parser.FaviconMapper import org.koitharu.kotatsu.core.parser.FaviconMapper
import org.koitharu.kotatsu.local.data.CacheDir
import org.koitharu.kotatsu.local.data.CbzFetcher import org.koitharu.kotatsu.local.data.CbzFetcher
val uiModule val uiModule
@@ -14,15 +16,23 @@ val uiModule
single { single {
val httpClientFactory = { val httpClientFactory = {
get<OkHttpClient>().newBuilder() get<OkHttpClient>().newBuilder()
.cache(CoilUtils.createDefaultCache(androidContext())) .cache(null)
.build()
}
val diskCacheFactory = {
val context = androidContext()
val rootDir = context.externalCacheDir ?: context.cacheDir
DiskCache.Builder()
.directory(rootDir.resolve(CacheDir.THUMBS.dir))
.build() .build()
} }
ImageLoader.Builder(androidContext()) ImageLoader.Builder(androidContext())
.okHttpClient(httpClientFactory) .okHttpClient(httpClientFactory)
.launchInterceptorChainOnMainThread(false) .interceptorDispatcher(Dispatchers.Default)
.componentRegistry( .diskCache(diskCacheFactory)
.components(
ComponentRegistry.Builder() ComponentRegistry.Builder()
.add(CbzFetcher()) .add(CbzFetcher.Factory())
.add(FaviconMapper()) .add(FaviconMapper())
.build() .build()
).build() ).build()

View File

@@ -283,7 +283,7 @@ class DetailsFragment :
.target(binding.imageViewCover) .target(binding.imageViewCover)
if (currentCover != null) { if (currentCover != null) {
request.data(manga.largeCoverUrl ?: return) request.data(manga.largeCoverUrl ?: return)
.placeholderMemoryCacheKey(CoilUtils.metadata(binding.imageViewCover)?.memoryCacheKey) .placeholderMemoryCacheKey(CoilUtils.result(binding.imageViewCover)?.request?.memoryCacheKey)
.fallback(currentCover) .fallback(currentCover)
} else { } else {
request.crossfade(true) request.crossfade(true)

View File

@@ -6,7 +6,6 @@ import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
@@ -14,7 +13,7 @@ import androidx.core.view.updatePadding
import coil.ImageLoader import coil.ImageLoader
import coil.request.CachePolicy import coil.request.CachePolicy
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.target.PoolableViewTarget import coil.target.ViewTarget
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 org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@@ -61,16 +60,12 @@ class ImageActivity : BaseActivity<ActivityImageBinding>() {
private class SsivTarget( private class SsivTarget(
override val view: SubsamplingScaleImageView, override val view: SubsamplingScaleImageView,
) : PoolableViewTarget<SubsamplingScaleImageView> { ) : ViewTarget<SubsamplingScaleImageView> {
override fun onStart(placeholder: Drawable?) = setDrawable(placeholder)
override fun onError(error: Drawable?) = setDrawable(error) override fun onError(error: Drawable?) = setDrawable(error)
override fun onSuccess(result: Drawable) = setDrawable(result) override fun onSuccess(result: Drawable) = setDrawable(result)
override fun onClear() = setDrawable(null)
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
return (this === other) || (other is SsivTarget && view == other.view) return (this === other) || (other is SsivTarget && view == other.view)
} }

View File

@@ -53,7 +53,7 @@ fun mangaGridItemAD(
badge = null badge = null
imageRequest?.dispose() imageRequest?.dispose()
imageRequest = null imageRequest = null
CoilUtils.clear(binding.imageViewCover) CoilUtils.dispose(binding.imageViewCover)
binding.imageViewCover.setImageDrawable(null) binding.imageViewCover.setImageDrawable(null)
} }
} }

View File

@@ -57,7 +57,7 @@ fun mangaListDetailedItemAD(
badge = null badge = null
imageRequest?.dispose() imageRequest?.dispose()
imageRequest = null imageRequest = null
CoilUtils.clear(binding.imageViewCover) CoilUtils.dispose(binding.imageViewCover)
binding.imageViewCover.setImageDrawable(null) binding.imageViewCover.setImageDrawable(null)
} }
} }

View File

@@ -55,7 +55,7 @@ fun mangaListItemAD(
badge = null badge = null
imageRequest?.dispose() imageRequest?.dispose()
imageRequest = null imageRequest = null
CoilUtils.clear(binding.imageViewCover) CoilUtils.dispose(binding.imageViewCover)
binding.imageViewCover.setImageDrawable(null) binding.imageViewCover.setImageDrawable(null)
} }
} }

View File

@@ -2,41 +2,52 @@ package org.koitharu.kotatsu.local.data
import android.net.Uri import android.net.Uri
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import coil.bitmap.BitmapPool import coil.ImageLoader
import coil.decode.DataSource import coil.decode.DataSource
import coil.decode.Options import coil.decode.ImageSource
import coil.fetch.FetchResult
import coil.fetch.Fetcher import coil.fetch.Fetcher
import coil.fetch.SourceResult import coil.fetch.SourceResult
import coil.size.Size import coil.request.Options
import java.util.zip.ZipFile
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.runInterruptible
import okio.buffer import okio.buffer
import okio.source import okio.source
import java.util.zip.ZipFile
class CbzFetcher : Fetcher<Uri> { class CbzFetcher(
private val uri: Uri,
private val options: Options
) : Fetcher {
override suspend fun fetch( override suspend fun fetch() = runInterruptible(Dispatchers.IO) {
pool: BitmapPool, val zip = ZipFile(uri.schemeSpecificPart)
data: Uri, val entry = zip.getEntry(uri.fragment)
size: Size,
options: Options,
): FetchResult = runInterruptible(Dispatchers.IO) {
val zip = ZipFile(data.schemeSpecificPart)
val entry = zip.getEntry(data.fragment)
val ext = MimeTypeMap.getFileExtensionFromUrl(entry.name) val ext = MimeTypeMap.getFileExtensionFromUrl(entry.name)
val bufferedSource = ExtraCloseableBufferedSource(
zip.getInputStream(entry).source().buffer(),
zip,
)
SourceResult( SourceResult(
source = ExtraCloseableBufferedSource( source = ImageSource(
zip.getInputStream(entry).source().buffer(), source = bufferedSource,
zip, context = options.context,
metadata = CbzMetadata(uri),
), ),
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext), mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext),
dataSource = DataSource.DISK dataSource = DataSource.DISK,
) )
} }
override fun key(data: Uri) = data.toString() class Factory : Fetcher.Factory<Uri> {
override fun handles(data: Uri) = data.scheme == "cbz" override fun create(data: Uri, options: Options, imageLoader: ImageLoader): Fetcher? {
return if (data.scheme == "cbz") {
CbzFetcher(data, options)
} else {
null
}
}
}
class CbzMetadata(val uri: Uri) : ImageSource.Metadata()
} }

View File

@@ -3,7 +3,7 @@ package org.koitharu.kotatsu.reader.ui.thumbnails.adapter
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import coil.ImageLoader import coil.ImageLoader
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.size.PixelSize import coil.size.Size
import com.google.android.material.R as materialR import com.google.android.material.R as materialR
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import kotlinx.coroutines.* import kotlinx.coroutines.*
@@ -27,7 +27,7 @@ fun pageThumbnailAD(
var job: Job? = null var job: Job? = null
val gridWidth = itemView.context.resources.getDimensionPixelSize(R.dimen.preferred_grid_width) val gridWidth = itemView.context.resources.getDimensionPixelSize(R.dimen.preferred_grid_width)
val thumbSize = PixelSize( val thumbSize = Size(
width = gridWidth, width = gridWidth,
height = (gridWidth * 13f / 18f).toInt() height = (gridWidth * 13f / 18f).toInt()
) )

View File

@@ -1,7 +1,8 @@
package org.koitharu.kotatsu.utils.progress package org.koitharu.kotatsu.utils.progress
import coil.request.ErrorResult
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.request.ImageResult import coil.request.SuccessResult
import com.google.android.material.progressindicator.BaseProgressIndicator import com.google.android.material.progressindicator.BaseProgressIndicator
class ImageRequestIndicatorListener( class ImageRequestIndicatorListener(
@@ -10,9 +11,9 @@ class ImageRequestIndicatorListener(
override fun onCancel(request: ImageRequest) = indicator.hide() override fun onCancel(request: ImageRequest) = indicator.hide()
override fun onError(request: ImageRequest, throwable: Throwable) = indicator.hide() override fun onError(request: ImageRequest, result: ErrorResult) = indicator.hide()
override fun onStart(request: ImageRequest) = indicator.show() override fun onStart(request: ImageRequest) = indicator.show()
override fun onSuccess(request: ImageRequest, metadata: ImageResult.Metadata) = indicator.hide() override fun onSuccess(request: ImageRequest, result: SuccessResult) = indicator.hide()
} }