diff --git a/app/build.gradle b/app/build.gradle index 5e8dfb9dd..c5c1bd44d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -59,7 +59,7 @@ android { '-opt-in=kotlinx.coroutines.ExperimentalForInheritanceCoroutinesApi', '-opt-in=kotlinx.coroutines.FlowPreview', '-opt-in=kotlin.contracts.ExperimentalContracts', - '-opt-in=coil.annotation.ExperimentalCoilApi', + '-opt-in=coil3.annotation.ExperimentalCoilApi', ] } lint { @@ -134,8 +134,9 @@ dependencies { implementation 'androidx.hilt:hilt-work:1.2.0' kapt 'androidx.hilt:hilt-compiler:1.2.0' - implementation 'io.coil-kt:coil-base:2.7.0' - implementation 'io.coil-kt:coil-svg:2.7.0' + implementation 'io.coil-kt.coil3:coil-core:3.0.0-rc01' + implementation 'io.coil-kt.coil3:coil-network-okhttp:3.0.0-rc01' + implementation 'io.coil-kt.coil3:coil-gif:3.0.0-rc01' implementation 'org.aomedia.avif.android:avif:1.1.1.14d8e3c4' implementation 'com.github.KotatsuApp:subsampling-scale-image-view:d1d10a6975' implementation 'com.github.solkin:disk-lru-cache:1.4' diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt index dc8b7875b..74afdba6a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt @@ -5,9 +5,16 @@ import androidx.core.content.ContextCompat import androidx.core.text.buildSpannedString import androidx.core.text.inSpans import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader -import coil.request.ImageRequest -import coil.transform.RoundedCornersTransformation +import coil3.ImageLoader +import coil3.request.ImageRequest +import coil3.request.allowRgb565 +import coil3.request.crossfade +import coil3.request.error +import coil3.request.fallback +import coil3.request.lifecycle +import coil3.request.placeholder +import coil3.request.transformations +import coil3.transform.RoundedCornersTransformation import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.getTitle @@ -19,8 +26,9 @@ import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaExtra +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemMangaAlternativeBinding import org.koitharu.kotatsu.list.ui.ListModelDiffCallback import org.koitharu.kotatsu.list.ui.model.ListModel @@ -74,7 +82,7 @@ fun alternativeAD( .placeholder(R.drawable.ic_web) .fallback(R.drawable.ic_web) .error(R.drawable.ic_web) - .source(item.manga.source) + .mangaSourceExtra(item.manga.source) .transformations(RoundedCornersTransformation(context.resources.getDimension(R.dimen.chip_icon_corner))) .allowRgb565(true) .enqueueWith(coil) @@ -84,8 +92,7 @@ fun alternativeAD( defaultPlaceholders(context) transformations(TrimTransformation()) allowRgb565(true) - tag(item.manga) - source(item.manga.source) + mangaExtra(item.manga) enqueueWith(coil) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt index 2f988d109..22a918928 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt @@ -8,7 +8,7 @@ import android.widget.Toast import androidx.activity.viewModels import androidx.core.graphics.Insets import androidx.core.view.updatePadding -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AutoFixService.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AutoFixService.kt index aa6f7d436..3ab970268 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AutoFixService.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AutoFixService.kt @@ -12,8 +12,8 @@ import androidx.core.app.NotificationManagerCompat import androidx.core.app.PendingIntentCompat import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat -import coil.ImageLoader -import coil.request.ImageRequest +import coil3.ImageLoader +import coil3.request.ImageRequest import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.runBlocking import org.koitharu.kotatsu.R @@ -23,6 +23,7 @@ import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.ui.CoroutineIntentService import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission import org.koitharu.kotatsu.core.util.ext.getDisplayMessage +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull import org.koitharu.kotatsu.details.ui.DetailsActivity @@ -121,7 +122,7 @@ class AutoFixService : CoroutineIntentService() { coil.execute( ImageRequest.Builder(applicationContext) .data(replacement.coverUrl) - .tag(replacement.source) + .mangaSourceExtra(replacement.source) .build(), ).toBitmapOrNull(), ) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksFragment.kt index 3a0182c70..5670eb82a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/AllBookmarksFragment.kt @@ -14,7 +14,7 @@ import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.fragment.app.viewModels import androidx.recyclerview.widget.GridLayoutManager -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.domain.Bookmark diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkLargeAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkLargeAD.kt index 00c0f6a2e..77fa1e9d8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkLargeAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkLargeAD.kt @@ -1,17 +1,18 @@ package org.koitharu.kotatsu.bookmarks.ui.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener +import org.koitharu.kotatsu.core.util.ext.bookmarkExtra import org.koitharu.kotatsu.core.util.ext.decodeRegion import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemBookmarkLargeBinding import org.koitharu.kotatsu.list.ui.model.ListModel @@ -29,9 +30,8 @@ fun bookmarkLargeAD( size(CoverSizeResolver(binding.imageViewThumb)) defaultPlaceholders(context) allowRgb565(true) - tag(item) + bookmarkExtra(item) decodeRegion(item.scroll) - source(item.manga.source) enqueueWith(coil) } binding.progressView.setProgress(item.percent, false) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt index 065bf84dc..d92f8a2da 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarkListAD.kt @@ -1,19 +1,21 @@ package org.koitharu.kotatsu.bookmarks.ui.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener +import org.koitharu.kotatsu.core.util.ext.bookmarkExtra import org.koitharu.kotatsu.core.util.ext.decodeRegion import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemBookmarkBinding +// TODO check usages fun bookmarkListAD( coil: ImageLoader, lifecycleOwner: LifecycleOwner, @@ -28,9 +30,8 @@ fun bookmarkListAD( size(CoverSizeResolver(binding.imageViewThumb)) defaultPlaceholders(context) allowRgb565(true) - tag(item) + bookmarkExtra(item) decodeRegion(item.scroll) - source(item.manga.source) enqueueWith(coil) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt index fcc980fd4..6bdee400c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/bookmarks/ui/adapter/BookmarksAdapter.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.bookmarks.ui.adapter import android.content.Context import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt index 83318f8ea..6df1955f2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt @@ -9,9 +9,10 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.app.PendingIntentCompat import androidx.core.net.toUri -import coil.EventListener -import coil.request.ErrorResult -import coil.request.ImageRequest +import coil3.EventListener +import coil3.Extras +import coil3.request.ErrorResult +import coil3.request.ImageRequest import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import org.koitharu.kotatsu.core.model.getTitle @@ -21,7 +22,7 @@ import org.koitharu.kotatsu.parsers.model.MangaSource class CaptchaNotifier( private val context: Context, -) : EventListener { +) : EventListener() { fun notify(exception: CloudFlareProtectedException) { if (!context.checkNotificationPermission(CHANNEL_ID)) { @@ -84,20 +85,19 @@ class CaptchaNotifier( override fun onError(request: ImageRequest, result: ErrorResult) { super.onError(request, result) val e = result.throwable - if (e is CloudFlareProtectedException && request.parameters.value(PARAM_IGNORE_CAPTCHA) != true) { + if (e is CloudFlareProtectedException && request.extras[ignoreCaptchaKey] != true) { notify(e) } } companion object { - fun ImageRequest.Builder.ignoreCaptchaErrors() = setParameter( - key = PARAM_IGNORE_CAPTCHA, - value = true, - memoryCacheKey = null, - ) + fun ImageRequest.Builder.ignoreCaptchaErrors() = apply { + extras[ignoreCaptchaKey] = true + } + + val ignoreCaptchaKey = Extras.Key(false) - private const val PARAM_IGNORE_CAPTCHA = "ignore_captcha" private const val CHANNEL_ID = "captcha" private const val TAG = CHANNEL_ID private const val GROUP_CAPTCHA = "org.koitharu.kotatsu.CAPTCHA" diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/AppModule.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/AppModule.kt index c0133b6e8..dc9dc8c38 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/AppModule.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/AppModule.kt @@ -2,16 +2,20 @@ package org.koitharu.kotatsu.core import android.app.Application import android.content.Context +import android.os.Build import android.provider.SearchRecentSuggestions import android.text.Html import androidx.collection.arraySetOf import androidx.room.InvalidationTracker import androidx.work.WorkManager -import coil.ComponentRegistry -import coil.ImageLoader -import coil.decode.SvgDecoder -import coil.disk.DiskCache -import coil.util.DebugLogger +import coil3.ImageLoader +import coil3.disk.DiskCache +import coil3.disk.directory +import coil3.gif.AnimatedImageDecoder +import coil3.gif.GifDecoder +import coil3.network.okhttp.OkHttpNetworkFetcherFactory +import coil3.request.allowRgb565 +import coil3.util.DebugLogger import dagger.Binds import dagger.Module import dagger.Provides @@ -28,6 +32,8 @@ import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.browser.cloudflare.CaptchaNotifier import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.image.AvifImageDecoder +import org.koitharu.kotatsu.core.image.CbzFetcher +import org.koitharu.kotatsu.core.image.MangaSourceHeaderInterceptor import org.koitharu.kotatsu.core.network.MangaHttpClient import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor import org.koitharu.kotatsu.core.os.AppShortcutManager @@ -44,7 +50,6 @@ import org.koitharu.kotatsu.core.util.ext.isLowRamDevice import org.koitharu.kotatsu.details.ui.pager.pages.MangaPageFetcher import org.koitharu.kotatsu.details.ui.pager.pages.MangaPageKeyer import org.koitharu.kotatsu.local.data.CacheDir -import org.koitharu.kotatsu.local.data.CbzFetcher import org.koitharu.kotatsu.local.data.LocalStorageChanges import org.koitharu.kotatsu.local.domain.model.LocalManga import org.koitharu.kotatsu.main.domain.CoverRestoreInterceptor @@ -92,6 +97,7 @@ interface AppModule { imageProxyInterceptor: ImageProxyInterceptor, pageFetcherFactory: MangaPageFetcher.Factory, coverRestoreInterceptor: CoverRestoreInterceptor, + networkStateProvider: Provider, ): ImageLoader { val diskCacheFactory = { val rootDir = context.externalCacheDir ?: context.cacheDir @@ -103,37 +109,38 @@ interface AppModule { okHttpClientProvider.get().newBuilder().cache(null).build() } return ImageLoader.Builder(context) - .okHttpClient { okHttpClientLazy.value } - .interceptorDispatcher(Dispatchers.Default) - .fetcherDispatcher(Dispatchers.Default) - .decoderDispatcher(Dispatchers.IO) - .transformationDispatcher(Dispatchers.Default) + .interceptorCoroutineContext(Dispatchers.Default) .diskCache(diskCacheFactory) - .respectCacheHeaders(false) - .networkObserverEnabled(false) .logger(if (BuildConfig.DEBUG) DebugLogger() else null) .allowRgb565(context.isLowRamDevice()) .eventListener(CaptchaNotifier(context)) - .components( - ComponentRegistry.Builder() - .add(SvgDecoder.Factory()) - .add(CbzFetcher.Factory()) - .add(AvifImageDecoder.Factory()) - .add(FaviconFetcher.Factory(context, okHttpClientLazy, mangaRepositoryFactory)) - .add(MangaPageKeyer()) - .add(pageFetcherFactory) - .add(imageProxyInterceptor) - .add(coverRestoreInterceptor) - .build(), - ).build() + .components { + add( + OkHttpNetworkFetcherFactory( + callFactory = okHttpClientLazy::value, + connectivityChecker = { networkStateProvider.get() }, + ), + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + add(AnimatedImageDecoder.Factory()) + } else { + add(GifDecoder.Factory()) + } + add(CbzFetcher.Factory()) + add(AvifImageDecoder.Factory()) + add(FaviconFetcher.Factory(mangaRepositoryFactory)) + add(MangaPageKeyer()) + add(pageFetcherFactory) + add(imageProxyInterceptor) + add(coverRestoreInterceptor) + add(MangaSourceHeaderInterceptor()) + }.build() } @Provides fun provideSearchSuggestions( @ApplicationContext context: Context, - ): SearchRecentSuggestions { - return MangaSuggestionsProvider.createSuggestions(context) - } + ): SearchRecentSuggestions = MangaSuggestionsProvider.createSuggestions(context) @Provides @ElementsIntoSet diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/AvifImageDecoder.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/AvifImageDecoder.kt index 9671baa73..140fea957 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/AvifImageDecoder.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/AvifImageDecoder.kt @@ -1,24 +1,25 @@ package org.koitharu.kotatsu.core.image import android.graphics.Bitmap -import android.graphics.BitmapFactory -import androidx.core.graphics.drawable.toDrawable -import coil.ImageLoader -import coil.decode.DecodeResult -import coil.decode.Decoder -import coil.decode.ImageSource -import coil.fetch.SourceResult -import coil.request.Options +import coil3.ImageLoader +import coil3.asImage +import coil3.decode.DecodeResult +import coil3.decode.Decoder +import coil3.decode.ImageSource +import coil3.fetch.SourceFetchResult +import coil3.request.Options import com.davemorrissey.labs.subscaleview.decoder.ImageDecodeException -import kotlinx.coroutines.sync.Semaphore +import kotlinx.coroutines.runInterruptible import org.aomedia.avif.android.AvifDecoder import org.aomedia.avif.android.AvifDecoder.Info import org.koitharu.kotatsu.core.util.ext.toByteBuffer -class AvifImageDecoder(source: ImageSource, options: Options, parallelismLock: Semaphore) : - BaseCoilDecoder(source, options, parallelismLock) { +class AvifImageDecoder( + private val source: ImageSource, + private val options: Options, +) : Decoder { - override fun BitmapFactory.Options.decode(): DecodeResult { + override suspend fun decode(): DecodeResult = runInterruptible { val bytes = source.source().use { it.inputStream().toByteBuffer() } @@ -36,22 +37,20 @@ class AvifImageDecoder(source: ImageSource, options: Options, parallelismLock: S bitmap.recycle() throw ImageDecodeException(null, "avif") } - return DecodeResult( - drawable = bitmap.toDrawable(options.context.resources), + DecodeResult( + image = bitmap.asImage(), isSampled = false, ) } class Factory : Decoder.Factory { - private val parallelismLock = Semaphore(DEFAULT_PARALLELISM) - override fun create( - result: SourceResult, + result: SourceFetchResult, options: Options, imageLoader: ImageLoader ): Decoder? = if (isApplicable(result)) { - AvifImageDecoder(result.source, options, parallelismLock) + AvifImageDecoder(result.source, options) } else { null } @@ -60,7 +59,7 @@ class AvifImageDecoder(source: ImageSource, options: Options, parallelismLock: S override fun hashCode() = javaClass.hashCode() - private fun isApplicable(result: SourceResult): Boolean { + private fun isApplicable(result: SourceFetchResult): Boolean { return result.mimeType == "image/avif" } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/BaseCoilDecoder.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/BaseCoilDecoder.kt deleted file mode 100644 index fc621ad49..000000000 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/BaseCoilDecoder.kt +++ /dev/null @@ -1,50 +0,0 @@ -package org.koitharu.kotatsu.core.image - -import android.graphics.BitmapFactory -import coil.decode.DecodeResult -import coil.decode.Decoder -import coil.decode.ImageSource -import coil.request.Options -import coil.size.Dimension -import coil.size.Scale -import coil.size.Size -import coil.size.isOriginal -import coil.size.pxOrElse -import kotlinx.coroutines.runInterruptible -import kotlinx.coroutines.sync.Semaphore -import kotlinx.coroutines.sync.withPermit -import org.jetbrains.annotations.Blocking - -abstract class BaseCoilDecoder( - protected val source: ImageSource, - protected val options: Options, - private val parallelismLock: Semaphore, -) : Decoder { - - final override suspend fun decode(): DecodeResult = parallelismLock.withPermit { - runInterruptible { BitmapFactory.Options().decode() } - } - - @Blocking - protected abstract fun BitmapFactory.Options.decode(): DecodeResult - - protected companion object { - - const val DEFAULT_PARALLELISM = 4 - - inline fun Size.widthPx(scale: Scale, original: () -> Int): Int { - return if (isOriginal) original() else width.toPx(scale) - } - - inline fun Size.heightPx(scale: Scale, original: () -> Int): Int { - return if (isOriginal) original() else height.toPx(scale) - } - - fun Dimension.toPx(scale: Scale) = pxOrElse { - when (scale) { - Scale.FILL -> Int.MIN_VALUE - Scale.FIT -> Int.MAX_VALUE - } - } - } -} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/CbzFetcher.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/CbzFetcher.kt new file mode 100644 index 000000000..9f52ff8c0 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/CbzFetcher.kt @@ -0,0 +1,48 @@ +package org.koitharu.kotatsu.core.image + +import android.net.Uri +import android.webkit.MimeTypeMap +import coil3.ImageLoader +import coil3.decode.DataSource +import coil3.decode.ImageSource +import coil3.fetch.Fetcher +import coil3.fetch.SourceFetchResult +import coil3.request.Options +import coil3.toAndroidUri +import kotlinx.coroutines.runInterruptible +import okio.Path.Companion.toPath +import okio.openZip +import org.koitharu.kotatsu.core.util.ext.isZipUri +import coil3.Uri as CoilUri + +class CbzFetcher( + private val uri: Uri, + private val options: Options, +) : Fetcher { + + override suspend fun fetch() = runInterruptible { + val filePath = uri.schemeSpecificPart.toPath() + val entryName = requireNotNull(uri.fragment) + SourceFetchResult( + source = ImageSource(entryName.toPath(), options.fileSystem.openZip(filePath)), + mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(entryName.substringAfterLast('.', "")), + dataSource = DataSource.DISK, + ) + } + + class Factory : Fetcher.Factory { + + override fun create( + data: CoilUri, + options: Options, + imageLoader: ImageLoader + ): Fetcher? { + val androidUri = data.toAndroidUri() + return if (androidUri.isZipUri()) { + CbzFetcher(androidUri, options) + } else { + null + } + } + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/MangaSourceHeaderInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/MangaSourceHeaderInterceptor.kt new file mode 100644 index 000000000..be4c068a4 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/MangaSourceHeaderInterceptor.kt @@ -0,0 +1,23 @@ +package org.koitharu.kotatsu.core.image + +import coil3.intercept.Interceptor +import coil3.network.httpHeaders +import coil3.request.ImageResult +import org.koitharu.kotatsu.core.network.CommonHeaders +import org.koitharu.kotatsu.core.util.ext.mangaSourceKey +import org.koitharu.kotatsu.parsers.model.MangaParserSource + +class MangaSourceHeaderInterceptor : Interceptor { + + override suspend fun intercept(chain: Interceptor.Chain): ImageResult { + val mangaSource = chain.request.extras[mangaSourceKey] as? MangaParserSource ?: return chain.proceed() + val request = chain.request + val newHeaders = request.httpHeaders.newBuilder() + .set(CommonHeaders.MANGA_SOURCE, mangaSource.name) + .build() + val newRequest = request.newBuilder() + .httpHeaders(newHeaders) + .build() + return chain.withRequest(newRequest).proceed() + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/RegionBitmapDecoder.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/RegionBitmapDecoder.kt index bff3abfba..c397c3b8d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/image/RegionBitmapDecoder.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/image/RegionBitmapDecoder.kt @@ -5,22 +5,35 @@ import android.graphics.BitmapFactory import android.graphics.BitmapRegionDecoder import android.graphics.Rect import android.os.Build -import androidx.core.graphics.drawable.toDrawable -import coil.ImageLoader -import coil.decode.DecodeResult -import coil.decode.DecodeUtils -import coil.decode.Decoder -import coil.decode.ImageSource -import coil.fetch.SourceResult -import coil.request.Options -import kotlinx.coroutines.sync.Semaphore +import coil3.Extras +import coil3.ImageLoader +import coil3.asImage +import coil3.decode.DecodeResult +import coil3.decode.DecodeUtils +import coil3.decode.Decoder +import coil3.decode.ImageSource +import coil3.fetch.SourceFetchResult +import coil3.getExtra +import coil3.request.Options +import coil3.request.allowRgb565 +import coil3.request.bitmapConfig +import coil3.request.colorSpace +import coil3.request.premultipliedAlpha +import coil3.size.Dimension +import coil3.size.Precision +import coil3.size.Scale +import coil3.size.Size +import coil3.size.isOriginal +import coil3.size.pxOrElse +import kotlinx.coroutines.runInterruptible import kotlin.math.roundToInt class RegionBitmapDecoder( - source: ImageSource, options: Options, parallelismLock: Semaphore -) : BaseCoilDecoder(source, options, parallelismLock) { + private val source: ImageSource, + private val options: Options, +) : Decoder { - override fun BitmapFactory.Options.decode(): DecodeResult { + override suspend fun decode(): DecodeResult = runInterruptible { val regionDecoder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { BitmapRegionDecoder.newInstance(source.source().inputStream()) } else { @@ -28,13 +41,14 @@ class RegionBitmapDecoder( BitmapRegionDecoder.newInstance(source.source().inputStream(), false) } checkNotNull(regionDecoder) + val bitmapOptions = BitmapFactory.Options() try { - val rect = configureScale(regionDecoder.width, regionDecoder.height) - configureConfig() - val bitmap = regionDecoder.decodeRegion(rect, this) + val rect = bitmapOptions.configureScale(regionDecoder.width, regionDecoder.height) + bitmapOptions.configureConfig() + val bitmap = regionDecoder.decodeRegion(rect, bitmapOptions) bitmap.density = options.context.resources.displayMetrics.densityDpi - return DecodeResult( - drawable = bitmap.toDrawable(options.context.resources), + DecodeResult( + image = bitmap.asImage(), isSampled = true, ) } finally { @@ -55,7 +69,7 @@ class RegionBitmapDecoder( } else { Rect(0, 0, (srcHeight / dstRatio).toInt().coerceAtLeast(1), srcHeight) } - val scroll = options.parameters.value(PARAM_SCROLL) ?: SCROLL_UNDEFINED + val scroll = options.getExtra(regionScrollKey) if (scroll == SCROLL_UNDEFINED) { rect.offsetTo( (srcWidth - rect.width()) / 2, @@ -87,7 +101,7 @@ class RegionBitmapDecoder( ) // Only upscale the image if the options require an exact size. - if (options.allowInexactSize) { + if (options.precision == Precision.INEXACT) { scale = scale.coerceAtMost(1.0) } @@ -107,7 +121,7 @@ class RegionBitmapDecoder( } private fun BitmapFactory.Options.configureConfig() { - var config = options.config + var config = options.bitmapConfig inMutable = false @@ -131,13 +145,11 @@ class RegionBitmapDecoder( object Factory : Decoder.Factory { - private val parallelismLock = Semaphore(DEFAULT_PARALLELISM) - override fun create( - result: SourceResult, + result: SourceFetchResult, options: Options, imageLoader: ImageLoader - ): Decoder = RegionBitmapDecoder(result.source, options, parallelismLock) + ): Decoder = RegionBitmapDecoder(result.source, options) override fun equals(other: Any?) = other is Factory @@ -146,7 +158,22 @@ class RegionBitmapDecoder( companion object { - const val PARAM_SCROLL = "scroll" const val SCROLL_UNDEFINED = -1 + val regionScrollKey = Extras.Key(SCROLL_UNDEFINED) + + private inline fun Size.widthPx(scale: Scale, original: () -> Int): Int { + return if (isOriginal) original() else width.toPx(scale) + } + + private inline fun Size.heightPx(scale: Scale, original: () -> Int): Int { + return if (isOriginal) original() else height.toPx(scale) + } + + private fun Dimension.toPx(scale: Scale) = pxOrElse { + when (scale) { + Scale.FILL -> Int.MIN_VALUE + Scale.FIT -> Int.MAX_VALUE + } + } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt index 9a31233a0..f63117f81 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeaders.kt @@ -16,6 +16,7 @@ object CommonHeaders { const val CACHE_CONTROL = "Cache-Control" const val PROXY_AUTHORIZATION = "Proxy-Authorization" const val RETRY_AFTER = "Retry-After" + const val MANGA_SOURCE = "X-Manga-Source" val CACHE_CONTROL_NO_STORE: CacheControl get() = CacheControl.Builder().noStore().build() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeadersInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeadersInterceptor.kt index 30ca3e95a..82d0148d5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeadersInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CommonHeadersInterceptor.kt @@ -9,11 +9,12 @@ import okhttp3.Request import okhttp3.Response import okio.IOException import org.koitharu.kotatsu.BuildConfig -import org.koitharu.kotatsu.core.model.UnknownMangaSource +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.parser.MangaLoaderContextImpl import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.ParserMangaRepository import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.mergeWith import org.koitharu.kotatsu.parsers.util.runCatchingCancellable @@ -30,15 +31,17 @@ class CommonHeadersInterceptor @Inject constructor( override fun intercept(chain: Chain): Response { val request = chain.request() val source = request.tag(MangaSource::class.java) - val repository = if (source == null || source == UnknownMangaSource) { + ?: request.headers[CommonHeaders.MANGA_SOURCE]?.let { MangaSource(it) } + val repository = if (source is MangaParserSource) { + mangaRepositoryFactoryLazy.get().create(source) as? ParserMangaRepository + } else { if (BuildConfig.DEBUG && source == null) { Log.w("Http", "Request without source tag: ${request.url}") } null - } else { - mangaRepositoryFactoryLazy.get().create(source) as? ParserMangaRepository } val headersBuilder = request.headers.newBuilder() + .removeAll(CommonHeaders.MANGA_SOURCE) repository?.getRequestHeaders()?.let { headersBuilder.mergeWith(it, replaceExisting = false) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/BaseImageProxyInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/BaseImageProxyInterceptor.kt index b88966367..ce7451008 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/BaseImageProxyInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/BaseImageProxyInterceptor.kt @@ -2,12 +2,12 @@ package org.koitharu.kotatsu.core.network.imageproxy import android.util.Log import androidx.collection.ArraySet -import coil.intercept.Interceptor -import coil.network.HttpException -import coil.request.ErrorResult -import coil.request.ImageRequest -import coil.request.ImageResult -import coil.request.SuccessResult +import coil3.intercept.Interceptor +import coil3.network.HttpException +import coil3.request.ErrorResult +import coil3.request.ImageRequest +import coil3.request.ImageResult +import coil3.request.SuccessResult import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.OkHttpClient @@ -35,14 +35,14 @@ abstract class BaseImageProxyInterceptor : ImageProxyInterceptor { else -> null } if (url == null || !url.isHttpOrHttps || url.host in blacklist) { - return chain.proceed(request) + return chain.proceed() } val newRequest = onInterceptImageRequest(request, url) - return when (val result = chain.proceed(newRequest)) { + return when (val result = chain.withRequest(newRequest).proceed()) { is SuccessResult -> result is ErrorResult -> { logDebug(result.throwable, newRequest.data) - chain.proceed(request).also { + chain.proceed().also { if (it is SuccessResult && result.throwable.isBlockedByServer()) { blacklist.add(url.host) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/ImageProxyInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/ImageProxyInterceptor.kt index 7b5e4b1bc..9d05aaf63 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/ImageProxyInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/ImageProxyInterceptor.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.core.network.imageproxy -import coil.intercept.Interceptor +import coil3.intercept.Interceptor import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/RealImageProxyInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/RealImageProxyInterceptor.kt index 2ade36803..c3b7cfe0b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/RealImageProxyInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/RealImageProxyInterceptor.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.core.network.imageproxy -import coil.intercept.Interceptor -import coil.request.ImageResult +import coil3.intercept.Interceptor +import coil3.request.ImageResult import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.plus import okhttp3.OkHttpClient @@ -26,7 +26,7 @@ class RealImageProxyInterceptor @Inject constructor( ) override suspend fun intercept(chain: Interceptor.Chain): ImageResult { - return delegate.value?.intercept(chain) ?: chain.proceed(chain.request) + return delegate.value?.intercept(chain) ?: chain.proceed() } override suspend fun interceptPageRequest(request: Request, okHttp: OkHttpClient): Response { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/WsrvNlProxyInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/WsrvNlProxyInterceptor.kt index 2645bf78c..13f821b9b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/WsrvNlProxyInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/WsrvNlProxyInterceptor.kt @@ -1,8 +1,8 @@ package org.koitharu.kotatsu.core.network.imageproxy -import coil.request.ImageRequest -import coil.size.Dimension -import coil.size.isOriginal +import coil3.request.ImageRequest +import coil3.size.Dimension +import coil3.size.isOriginal import okhttp3.HttpUrl import okhttp3.Request diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/ZeroMsProxyInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/ZeroMsProxyInterceptor.kt index ba7670d67..2fe1f4f6d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/ZeroMsProxyInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/imageproxy/ZeroMsProxyInterceptor.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.core.network.imageproxy -import coil.request.ImageRequest +import coil3.request.ImageRequest import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Request diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt index 414a8a24f..e406d8e55 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt @@ -10,10 +10,11 @@ import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.toBitmap import androidx.room.InvalidationTracker -import coil.ImageLoader -import coil.request.ImageRequest -import coil.size.Scale -import coil.size.Size +import coil3.ImageLoader +import coil3.request.ImageRequest +import coil3.request.transformations +import coil3.size.Scale +import coil3.size.Size import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -27,9 +28,9 @@ import org.koitharu.kotatsu.core.parser.favicon.faviconUri import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.image.ThumbnailTransformation import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.processLifecycleScope -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource @@ -138,7 +139,7 @@ class AppShortcutManager @Inject constructor( ImageRequest.Builder(context) .data(manga.coverUrl) .size(iconSize) - .source(manga.source) + .mangaSourceExtra(manga.source) .scale(Scale.FILL) .transformations(ThumbnailTransformation()) .build(), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/NetworkState.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/NetworkState.kt index 564db96aa..0d98d2368 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/NetworkState.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/NetworkState.kt @@ -6,6 +6,7 @@ import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest import android.os.Build +import coil3.network.ConnectivityChecker import kotlinx.coroutines.flow.first import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.MediatorStateFlow @@ -13,13 +14,17 @@ import org.koitharu.kotatsu.core.util.MediatorStateFlow class NetworkState( private val connectivityManager: ConnectivityManager, private val settings: AppSettings, -) : MediatorStateFlow(connectivityManager.isOnline(settings)) { +) : MediatorStateFlow(connectivityManager.isOnline(settings)), ConnectivityChecker { private val callback = NetworkCallbackImpl() override val value: Boolean get() = connectivityManager.isOnline(settings) + override fun isOnline(): Boolean { + return connectivityManager.isOnline(settings) + } + @Synchronized override fun onActive() { invalidate() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/CachingMangaRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/CachingMangaRepository.kt index 261f78900..6c55dfd3d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/CachingMangaRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/CachingMangaRepository.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.core.parser import android.util.Log import androidx.collection.MutableLongSet -import coil.request.CachePolicy +import coil3.request.CachePolicy import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLinkResolver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLinkResolver.kt index 60a4f183b..c831b1623 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLinkResolver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLinkResolver.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.core.parser import android.net.Uri -import coil.request.CachePolicy +import coil3.request.CachePolicy import dagger.Reusable import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.UnknownMangaSource diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt index 1d28509c7..fb7753d9b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt @@ -1,6 +1,5 @@ package org.koitharu.kotatsu.core.parser.favicon -import android.content.Context import android.graphics.Color import android.graphics.drawable.AdaptiveIconDrawable import android.graphics.drawable.ColorDrawable @@ -8,67 +7,43 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.net.Uri import android.os.Build -import android.webkit.MimeTypeMap -import coil.ImageLoader -import coil.decode.DataSource -import coil.decode.ImageSource -import coil.disk.DiskCache -import coil.fetch.DrawableResult -import coil.fetch.FetchResult -import coil.fetch.Fetcher -import coil.fetch.SourceResult -import coil.network.HttpException -import coil.request.Options -import coil.size.Size -import coil.size.pxOrElse -import kotlinx.coroutines.Dispatchers +import coil3.ImageLoader +import coil3.asImage +import coil3.decode.DataSource +import coil3.fetch.FetchResult +import coil3.fetch.Fetcher +import coil3.fetch.ImageFetchResult +import coil3.request.Options +import coil3.size.pxOrElse +import coil3.toAndroidUri import kotlinx.coroutines.ensureActive import kotlinx.coroutines.runInterruptible -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import okhttp3.ResponseBody -import okhttp3.internal.closeQuietly -import okio.Closeable -import okio.buffer +import okio.IOException import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.parser.EmptyMangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.ParserMangaRepository import org.koitharu.kotatsu.core.parser.external.ExternalMangaRepository -import org.koitharu.kotatsu.core.util.ext.writeAllCancellable -import org.koitharu.kotatsu.local.data.CacheDir -import org.koitharu.kotatsu.local.data.util.withExtraCloseable -import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.parsers.util.await -import org.koitharu.kotatsu.parsers.util.requireBody -import java.net.HttpURLConnection +import org.koitharu.kotatsu.core.util.ext.fetch import kotlin.coroutines.coroutineContext - -private const val FALLBACK_SIZE = 9999 // largest icon +import coil3.Uri as CoilUri class FaviconFetcher( - private val okHttpClient: OkHttpClient, - private val diskCache: Lazy, - private val mangaSource: MangaSource, + private val uri: Uri, private val options: Options, + private val imageLoader: ImageLoader, private val mangaRepositoryFactory: MangaRepository.Factory, ) : Fetcher { - private val diskCacheKey - get() = options.diskCacheKey ?: "${mangaSource.name}x${options.size.toCacheKey()}" - - private val fileSystem - get() = checkNotNull(diskCache.value).fileSystem - override suspend fun fetch(): FetchResult { - getCached(options)?.let { return it } + val mangaSource = MangaSource(uri.schemeSpecificPart) + return when (val repo = mangaRepositoryFactory.create(mangaSource)) { is ParserMangaRepository -> fetchParserFavicon(repo) is ExternalMangaRepository -> fetchPluginIcon(repo) - is EmptyMangaRepository -> DrawableResult( - drawable = ColorDrawable(Color.WHITE), + is EmptyMangaRepository -> ImageFetchResult( + image = ColorDrawable(Color.WHITE).asImage(), isSampled = false, dataSource = DataSource.MEMORY, ) @@ -77,163 +52,81 @@ class FaviconFetcher( } } - private suspend fun fetchParserFavicon(repo: ParserMangaRepository): FetchResult { + private suspend fun fetchParserFavicon(repository: ParserMangaRepository): FetchResult { val sizePx = maxOf( options.size.width.pxOrElse { FALLBACK_SIZE }, options.size.height.pxOrElse { FALLBACK_SIZE }, ) - var favicons = repo.getFavicons() + var favicons = repository.getFavicons() var lastError: Exception? = null while (favicons.isNotEmpty()) { coroutineContext.ensureActive() val icon = favicons.find(sizePx) ?: throwNSEE(lastError) - val response = try { - loadIcon(icon.url, mangaSource) + try { + val result = imageLoader.fetch(icon.url, options) + if (result != null) { + return result + } else { + favicons -= icon + } } catch (e: CloudFlareProtectedException) { throw e - } catch (e: HttpException) { + } catch (e: IOException) { lastError = e favicons -= icon - continue } - val responseBody = response.requireBody() - val source = writeToDiskCache(responseBody)?.toImageSource()?.also { - response.closeQuietly() - } ?: responseBody.toImageSource(response) - return SourceResult( - source = source, - mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(icon.type), - dataSource = response.toDataSource(), - ) } throwNSEE(lastError) } - private suspend fun loadIcon(url: String, source: MangaSource): Response { - val request = Request.Builder() - .url(url) - .get() - .tag(MangaSource::class.java, source) - request.tag(MangaSource::class.java, source) - @Suppress("UNCHECKED_CAST") - options.tags.asMap().forEach { request.tag(it.key as Class, it.value) } - val response = okHttpClient.newCall(request.build()).await() - if (!response.isSuccessful && response.code != HttpURLConnection.HTTP_NOT_MODIFIED) { - response.closeQuietly() - throw HttpException(response) - } - return response - } - private suspend fun fetchPluginIcon(repository: ExternalMangaRepository): FetchResult { val source = repository.source val pm = options.context.packageManager - val icon = runInterruptible(Dispatchers.IO) { + val icon = runInterruptible { val provider = pm.resolveContentProvider(source.authority, 0) provider?.loadIcon(pm) ?: pm.getApplicationIcon(source.packageName) } - return DrawableResult( - drawable = icon.nonAdaptive(), + return ImageFetchResult( + image = icon.nonAdaptive().asImage(), isSampled = false, dataSource = DataSource.DISK, ) } - private fun getCached(options: Options): SourceResult? { - if (!options.diskCachePolicy.readEnabled) { - return null - } - val snapshot = diskCache.value?.openSnapshot(diskCacheKey) ?: return null - return SourceResult( - source = snapshot.toImageSource(), - mimeType = null, - dataSource = DataSource.DISK, - ) - } - - private suspend fun writeToDiskCache(body: ResponseBody): DiskCache.Snapshot? { - if (!options.diskCachePolicy.writeEnabled || body.contentLength() == 0L) { - return null - } - val editor = diskCache.value?.openEditor(diskCacheKey) ?: return null - try { - fileSystem.write(editor.data) { - writeAllCancellable(body.source()) - } - return editor.commitAndOpenSnapshot() - } catch (e: Throwable) { - try { - editor.abort() - } catch (abortingError: Throwable) { - e.addSuppressed(abortingError) - } - body.closeQuietly() - throw e - } finally { - body.closeQuietly() - } - } - - private fun DiskCache.Snapshot.toImageSource(): ImageSource { - return ImageSource(data, fileSystem, diskCacheKey, this) - } - - private fun ResponseBody.toImageSource(response: Closeable): ImageSource { - return ImageSource( - source().withExtraCloseable(response).buffer(), - options.context, - FaviconMetadata(mangaSource), - ) - } - - private fun Response.toDataSource(): DataSource { - return if (networkResponse != null) DataSource.NETWORK else DataSource.DISK - } - - private fun Size.toCacheKey() = buildString { - append(width.toString()) - append('x') - append(height.toString()) - } - - private fun throwNSEE(lastError: Exception?): Nothing { - if (lastError != null) { - throw lastError + class Factory( + private val mangaRepositoryFactory: MangaRepository.Factory, + ) : Fetcher.Factory { + + override fun create( + data: CoilUri, + options: Options, + imageLoader: ImageLoader + ): Fetcher? = if (data.scheme == URI_SCHEME_FAVICON) { + FaviconFetcher(data.toAndroidUri(), options, imageLoader, mangaRepositoryFactory) } else { - throw NoSuchElementException("No favicons found") + null } } - private fun Drawable.nonAdaptive() = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this is AdaptiveIconDrawable) { - LayerDrawable(arrayOf(background, foreground)) - } else { - this - } + private companion object { - class Factory( - context: Context, - okHttpClientLazy: Lazy, - private val mangaRepositoryFactory: MangaRepository.Factory, - ) : Fetcher.Factory { + const val FALLBACK_SIZE = 9999 // largest icon - private val okHttpClient by okHttpClientLazy - private val diskCache = lazy { - val rootDir = context.externalCacheDir ?: context.cacheDir - DiskCache.Builder() - .directory(rootDir.resolve(CacheDir.FAVICONS.dir)) - .build() + private fun throwNSEE(lastError: Exception?): Nothing { + if (lastError != null) { + throw lastError + } else { + throw NoSuchElementException("No favicons found") + } } - override fun create(data: Uri, options: Options, imageLoader: ImageLoader): Fetcher? { - return if (data.scheme == URI_SCHEME_FAVICON) { - val mangaSource = MangaSource(data.schemeSpecificPart) - FaviconFetcher(okHttpClient, diskCache, mangaSource, options, mangaRepositoryFactory) + private fun Drawable.nonAdaptive() = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this is AdaptiveIconDrawable) { + LayerDrawable(arrayOf(background, foreground)) } else { - null + this } - } - } - class FaviconMetadata(val source: MangaSource) : ImageSource.Metadata() + } } + diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ChipIconTarget.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ChipIconTarget.kt index 82b002b86..1899b7977 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ChipIconTarget.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ChipIconTarget.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.core.ui.image import android.graphics.drawable.Drawable -import coil.target.GenericViewTarget +import coil3.target.GenericViewTarget import com.google.android.material.chip.Chip class ChipIconTarget(override val view: Chip) : GenericViewTarget() { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/CoilImageGetter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/CoilImageGetter.kt index 7c5a5467f..9ab5efc45 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/CoilImageGetter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/CoilImageGetter.kt @@ -4,10 +4,12 @@ import android.content.Context import android.graphics.drawable.Drawable import android.text.Html import androidx.annotation.WorkerThread -import coil.ImageLoader -import coil.executeBlocking -import coil.request.ImageRequest +import coil3.ImageLoader +import coil3.executeBlocking +import coil3.request.ImageRequest +import coil3.request.allowHardware import dagger.hilt.android.qualifiers.ApplicationContext +import org.koitharu.kotatsu.core.util.ext.drawable import javax.inject.Inject class CoilImageGetter @Inject constructor( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/CoverSizeResolver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/CoverSizeResolver.kt index b7d7bf63e..d90134da7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/CoverSizeResolver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/CoverSizeResolver.kt @@ -4,9 +4,9 @@ import android.view.View import android.view.View.OnLayoutChangeListener import android.view.ViewGroup import android.widget.ImageView -import coil.size.Dimension -import coil.size.Size -import coil.size.ViewSizeResolver +import coil3.size.Dimension +import coil3.size.Size +import coil3.size.ViewSizeResolver import kotlinx.coroutines.CancellableContinuation import kotlinx.coroutines.suspendCancellableCoroutine import kotlin.coroutines.resume diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ThumbnailTransformation.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ThumbnailTransformation.kt index c0ca38662..b27cc0166 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ThumbnailTransformation.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ThumbnailTransformation.kt @@ -2,11 +2,11 @@ package org.koitharu.kotatsu.core.ui.image import android.graphics.Bitmap import android.media.ThumbnailUtils -import coil.size.Size -import coil.size.pxOrElse -import coil.transform.Transformation +import coil3.size.Size +import coil3.size.pxOrElse +import coil3.transform.Transformation -class ThumbnailTransformation : Transformation { +class ThumbnailTransformation : Transformation() { override val cacheKey: String = javaClass.name @@ -17,8 +17,4 @@ class ThumbnailTransformation : Transformation { size.height.pxOrElse { input.height }, ) } - - override fun equals(other: Any?) = other is ThumbnailTransformation - - override fun hashCode() = javaClass.hashCode() } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/TrimTransformation.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/TrimTransformation.kt index 15695ff11..40d0c2824 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/TrimTransformation.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/TrimTransformation.kt @@ -2,13 +2,13 @@ package org.koitharu.kotatsu.core.ui.image import android.graphics.Bitmap import androidx.core.graphics.get -import coil.size.Size -import coil.transform.Transformation +import coil3.size.Size +import coil3.transform.Transformation import org.koitharu.kotatsu.reader.domain.EdgeDetector.Companion.isColorTheSame class TrimTransformation( private val tolerance: Int = 20, -) : Transformation { +) : Transformation() { override val cacheKey: String = "${javaClass.name}-$tolerance" @@ -92,12 +92,4 @@ class TrimTransformation( input } } - - override fun equals(other: Any?): Boolean { - return this === other || (other is TrimTransformation && other.tolerance == tolerance) - } - - override fun hashCode(): Int { - return tolerance - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ChipsView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ChipsView.kt index d22681ecb..3a5d98268 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ChipsView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/ChipsView.kt @@ -8,10 +8,16 @@ import androidx.annotation.ColorRes import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.core.view.children -import coil.ImageLoader -import coil.request.Disposable -import coil.request.ImageRequest -import coil.transform.RoundedCornersTransformation +import coil3.ImageLoader +import coil3.request.Disposable +import coil3.request.ImageRequest +import coil3.request.allowRgb565 +import coil3.request.crossfade +import coil3.request.error +import coil3.request.fallback +import coil3.request.placeholder +import coil3.request.transformations +import coil3.transform.RoundedCornersTransformation import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipDrawable import com.google.android.material.chip.ChipGroup diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Coil.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Coil.kt index c50b0daf0..6bfbffb75 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Coil.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Coil.kt @@ -2,21 +2,37 @@ package org.koitharu.kotatsu.core.util.ext import android.content.Context import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable import android.widget.ImageView import androidx.core.graphics.ColorUtils -import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader -import coil.request.ErrorResult -import coil.request.ImageRequest -import coil.request.ImageResult -import coil.request.SuccessResult -import coil.util.CoilUtils +import coil3.Extras +import coil3.ImageLoader +import coil3.asDrawable +import coil3.fetch.FetchResult +import coil3.request.ErrorResult +import coil3.request.ImageRequest +import coil3.request.ImageResult +import coil3.request.Options +import coil3.request.SuccessResult +import coil3.request.bitmapConfig +import coil3.request.crossfade +import coil3.request.error +import coil3.request.fallback +import coil3.request.lifecycle +import coil3.request.placeholder +import coil3.request.target +import coil3.size.Scale +import coil3.size.ViewSizeResolver +import coil3.toBitmap +import coil3.util.CoilUtils import com.google.android.material.progressindicator.BaseProgressIndicator import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.core.image.RegionBitmapDecoder import org.koitharu.kotatsu.core.ui.image.AnimatedPlaceholderDrawable import org.koitharu.kotatsu.core.util.progress.ImageRequestIndicatorListener +import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource import com.google.android.material.R as materialR @@ -32,6 +48,8 @@ fun ImageView.newImageRequest(lifecycleOwner: LifecycleOwner, data: Any?): Image .data(data?.takeUnless { it == "" || it == 0 }) .lifecycle(lifecycleOwner) .crossfade(context) + .size(ViewSizeResolver(this)) + .scale(scaleType.toCoilScale()) .target(this) } @@ -43,13 +61,16 @@ fun ImageView.disposeImageRequest() { fun ImageRequest.Builder.enqueueWith(loader: ImageLoader) = loader.enqueue(build()) fun ImageResult.getDrawableOrThrow() = when (this) { - is SuccessResult -> drawable + is SuccessResult -> image.asDrawable(request.context.resources) is ErrorResult -> throw throwable } +val ImageResult.drawable: Drawable? + get() = image?.asDrawable(request.context.resources) + fun ImageResult.toBitmapOrNull() = when (this) { is SuccessResult -> try { - drawable.toBitmap() + image.toBitmap(image.width, image.height, request.bitmapConfig) } catch (_: Throwable) { null } @@ -63,8 +84,10 @@ fun ImageRequest.Builder.indicator(indicators: List>): fun ImageRequest.Builder.decodeRegion( scroll: Int = RegionBitmapDecoder.SCROLL_UNDEFINED, -): ImageRequest.Builder = decoderFactory(RegionBitmapDecoder.Factory) - .setParameter(RegionBitmapDecoder.PARAM_SCROLL, scroll) +): ImageRequest.Builder = apply { + decoderFactory(RegionBitmapDecoder.Factory) + extras[RegionBitmapDecoder.regionScrollKey] = scroll +} @Suppress("SpellCheckingInspection") fun ImageRequest.Builder.crossfade(context: Context): ImageRequest.Builder { @@ -72,8 +95,18 @@ fun ImageRequest.Builder.crossfade(context: Context): ImageRequest.Builder { return crossfade(duration.toInt()) } -fun ImageRequest.Builder.source(source: MangaSource?): ImageRequest.Builder { - return tag(MangaSource::class.java, source) +fun ImageRequest.Builder.mangaSourceExtra(source: MangaSource?): ImageRequest.Builder = apply { + extras[mangaSourceKey] = source +} + +fun ImageRequest.Builder.mangaExtra(manga: Manga): ImageRequest.Builder = apply { + extras[mangaKey] = manga + mangaSourceExtra(manga.source) +} + +fun ImageRequest.Builder.bookmarkExtra(bookmark: Bookmark): ImageRequest.Builder = apply { + extras[bookmarkKey] = bookmark + mangaSourceExtra(bookmark.manga.source) } fun ImageRequest.Builder.defaultPlaceholders(context: Context): ImageRequest.Builder { @@ -87,6 +120,12 @@ fun ImageRequest.Builder.defaultPlaceholders(context: Context): ImageRequest.Bui .error(ColorDrawable(errorColor)) } +private fun ImageView.ScaleType.toCoilScale(): Scale = if (this == ImageView.ScaleType.CENTER_CROP) { + Scale.FILL +} else { + Scale.FIT +} + fun ImageRequest.Builder.addListener(listener: ImageRequest.Listener): ImageRequest.Builder { val existing = build().listener return listener( @@ -98,6 +137,12 @@ fun ImageRequest.Builder.addListener(listener: ImageRequest.Listener): ImageRequ ) } +suspend fun ImageLoader.fetch(data: Any, options: Options): FetchResult? { + val mappedData = components.map(data, options) + val fetcher = components.newFetcher(mappedData, options, this)?.first + return fetcher?.fetch() +} + private class CompositeImageRequestListener( private val delegates: Array, ) : ImageRequest.Listener { @@ -113,3 +158,7 @@ private class CompositeImageRequestListener( operator fun plus(other: ImageRequest.Listener) = CompositeImageRequestListener(delegates + other) } + +val mangaKey = Extras.Key(null) +val bookmarkKey = Extras.Key(null) +val mangaSourceKey = Extras.Key(null) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt index ca7840a9b..dd6e8e8d9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/File.kt @@ -61,7 +61,7 @@ fun File.getStorageName(context: Context): String = runCatching { } }.getOrNull() ?: context.getString(R.string.other_storage) -fun Uri.toFileOrNull() = if (scheme == URI_SCHEME_FILE) path?.let(::File) else null +fun Uri.toFileOrNull() = if (isFileUri()) path?.let(::File) else null suspend fun File.deleteAwait() = runInterruptible(Dispatchers.IO) { delete() || deleteRecursively() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt index 41a51ce63..d6d389718 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt @@ -3,8 +3,9 @@ package org.koitharu.kotatsu.core.util.ext import android.content.ActivityNotFoundException import android.content.res.Resources import androidx.annotation.DrawableRes -import coil.network.HttpException +import coil3.network.HttpException import com.davemorrissey.labs.subscaleview.decoder.ImageDecodeException +import okhttp3.Response import okio.FileNotFoundException import okio.IOException import okio.ProtocolException @@ -121,7 +122,7 @@ fun Throwable.getCauseUrl(): String? = when (this) { is CloudFlareBlockedException -> url is CloudFlareProtectedException -> url is HttpStatusException -> url - is HttpException -> response.request.url.toString() + is HttpException -> (response.delegate as? Response)?.request?.url?.toString() else -> null } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Uri.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Uri.kt index 5664a180d..d03fb6617 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Uri.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Uri.kt @@ -1,62 +1,25 @@ package org.koitharu.kotatsu.core.util.ext import android.net.Uri -import androidx.core.net.toFile -import okhttp3.internal.closeQuietly -import okio.Source -import okio.source -import okio.use -import org.jetbrains.annotations.Blocking -import org.koitharu.kotatsu.local.data.util.withExtraCloseable import java.io.File -import java.util.zip.ZipFile -const val URI_SCHEME_FILE = "file" const val URI_SCHEME_ZIP = "file+zip" - -@Blocking -fun Uri.exists(): Boolean = when (scheme) { - URI_SCHEME_FILE -> toFile().exists() - URI_SCHEME_ZIP -> { - val file = File(requireNotNull(schemeSpecificPart)) - file.exists() && ZipFile(file).use { it.getEntry(fragment) != null } - } - - else -> unsupportedUri(this) +private const val URI_SCHEME_FILE = "file" +private const val URI_SCHEME_HTTP = "http" +private const val URI_SCHEME_HTTPS = "https" +private const val URI_SCHEME_LEGACY_CBZ = "cbz" +private const val URI_SCHEME_LEGACY_ZIP = "zip" + +fun Uri.isZipUri() = scheme.let { + it == URI_SCHEME_ZIP || it == URI_SCHEME_LEGACY_CBZ || it == URI_SCHEME_LEGACY_ZIP } -@Blocking -fun Uri.isTargetNotEmpty(): Boolean = when (scheme) { - URI_SCHEME_FILE -> toFile().isNotEmpty() - URI_SCHEME_ZIP -> { - val file = File(requireNotNull(schemeSpecificPart)) - file.exists() && ZipFile(file).use { (it.getEntry(fragment)?.size ?: 0L) != 0L } - } +fun Uri.isFileUri() = scheme == URI_SCHEME_FILE - else -> unsupportedUri(this) -} - -@Blocking -fun Uri.source(): Source = when (scheme) { - URI_SCHEME_FILE -> toFile().source() - URI_SCHEME_ZIP -> { - val zip = ZipFile(schemeSpecificPart) - try { - val entry = zip.getEntry(fragment) - zip.getInputStream(entry).source().withExtraCloseable(zip) - } catch (e: Throwable) { - zip.closeQuietly() - throw e - } - } - - else -> unsupportedUri(this) +fun Uri.isNetworkUri() = scheme.let { + it == URI_SCHEME_HTTP || it == URI_SCHEME_HTTPS } fun File.toZipUri(entryName: String): Uri = Uri.parse("$URI_SCHEME_ZIP://$absolutePath#$entryName") fun String.toUriOrNull() = if (isEmpty()) null else Uri.parse(this) - -private fun unsupportedUri(uri: Uri): Nothing { - throw IllegalArgumentException("Bad uri $uri: only schemes $URI_SCHEME_FILE and $URI_SCHEME_ZIP are supported") -} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/progress/ImageRequestIndicatorListener.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/progress/ImageRequestIndicatorListener.kt index 5b2d5bee8..fbc69a11d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/progress/ImageRequestIndicatorListener.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/progress/ImageRequestIndicatorListener.kt @@ -1,8 +1,8 @@ package org.koitharu.kotatsu.core.util.progress -import coil.request.ErrorResult -import coil.request.ImageRequest -import coil.request.SuccessResult +import coil3.request.ErrorResult +import coil3.request.ImageRequest +import coil3.request.SuccessResult import com.google.android.material.progressindicator.BaseProgressIndicator class ImageRequestIndicatorListener( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt index 8c45b23ce..7b9cb3e8c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt @@ -26,11 +26,20 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.swiperefreshlayout.widget.CircularProgressDrawable -import coil.ImageLoader -import coil.request.ImageRequest -import coil.request.SuccessResult -import coil.transform.RoundedCornersTransformation -import coil.util.CoilUtils +import coil3.ImageLoader +import coil3.request.ImageRequest +import coil3.request.SuccessResult +import coil3.request.allowRgb565 +import coil3.request.crossfade +import coil3.request.error +import coil3.request.fallback +import coil3.request.lifecycle +import coil3.request.placeholder +import coil3.request.target +import coil3.request.transformations +import coil3.size.Scale +import coil3.transform.RoundedCornersTransformation +import coil3.util.CoilUtils import com.google.android.material.chip.Chip import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint @@ -65,18 +74,19 @@ import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.core.util.FileSize import org.koitharu.kotatsu.core.util.ext.crossfade import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders +import org.koitharu.kotatsu.core.util.ext.drawable import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.getThemeColor import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty import org.koitharu.kotatsu.core.util.ext.isTextTruncated import org.koitharu.kotatsu.core.util.ext.joinToStringWithLimit +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.parentView import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf import org.koitharu.kotatsu.core.util.ext.setNavigationBarTransparentCompat import org.koitharu.kotatsu.core.util.ext.setOnContextClickListenerCompat -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.ActivityDetailsBinding import org.koitharu.kotatsu.details.data.MangaDetails @@ -485,7 +495,7 @@ class DetailsActivity : .placeholder(R.drawable.ic_web) .fallback(R.drawable.ic_web) .error(R.drawable.ic_web) - .source(manga.source) + .mangaSourceExtra(manga.source) .transformations(RoundedCornersTransformation(resources.getDimension(R.dimen.chip_icon_corner))) .allowRgb565(true) .enqueueWith(coil) @@ -621,8 +631,9 @@ class DetailsActivity : val request = ImageRequest.Builder(this) .target(viewBinding.imageViewCover) .size(CoverSizeResolver(viewBinding.imageViewCover)) + .scale(Scale.FILL) .data(imageUrl) - .tag(manga.source) + .mangaSourceExtra(manga.source) .crossfade(this) .lifecycle(this) .placeholderMemoryCacheKey(manga.coverUrl) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/BookmarksFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/BookmarksFragment.kt index faa8c4069..b554b645a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/BookmarksFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/bookmarks/BookmarksFragment.kt @@ -11,7 +11,7 @@ import androidx.appcompat.view.ActionMode import androidx.core.graphics.Insets import androidx.fragment.app.viewModels import androidx.recyclerview.widget.GridLayoutManager -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.domain.Bookmark diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageFetcher.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageFetcher.kt index fd065ee2e..62e5fb898 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageFetcher.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageFetcher.kt @@ -1,60 +1,53 @@ package org.koitharu.kotatsu.details.ui.pager.pages -import android.content.Context import android.webkit.MimeTypeMap -import androidx.core.net.toFile import androidx.core.net.toUri -import coil.ImageLoader -import coil.decode.DataSource -import coil.decode.ImageSource -import coil.fetch.FetchResult -import coil.fetch.Fetcher -import coil.fetch.SourceResult -import coil.network.HttpException -import coil.request.Options -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runInterruptible +import coil3.ImageLoader +import coil3.decode.DataSource +import coil3.decode.ImageSource +import coil3.fetch.FetchResult +import coil3.fetch.Fetcher +import coil3.fetch.SourceFetchResult +import coil3.network.HttpException +import coil3.network.NetworkHeaders +import coil3.network.NetworkResponse +import coil3.network.NetworkResponseBody +import coil3.request.Options +import okhttp3.Headers import okhttp3.OkHttpClient -import okhttp3.internal.closeQuietly +import okhttp3.Response +import okio.FileSystem import okio.Path.Companion.toOkioPath -import okio.buffer -import okio.source import org.koitharu.kotatsu.core.network.MangaHttpClient import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.util.ext.fetch +import org.koitharu.kotatsu.core.util.ext.isNetworkUri import org.koitharu.kotatsu.local.data.PagesCache -import org.koitharu.kotatsu.local.data.isFileUri -import org.koitharu.kotatsu.local.data.isZipUri -import org.koitharu.kotatsu.local.data.util.withExtraCloseable import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.util.mimeType import org.koitharu.kotatsu.parsers.util.requireBody import org.koitharu.kotatsu.reader.domain.PageLoader -import java.util.zip.ZipFile import javax.inject.Inject class MangaPageFetcher( - private val context: Context, private val okHttpClient: OkHttpClient, private val pagesCache: PagesCache, private val options: Options, private val page: MangaPage, private val mangaRepositoryFactory: MangaRepository.Factory, private val imageProxyInterceptor: ImageProxyInterceptor, + private val imageLoader: ImageLoader, ) : Fetcher { - override suspend fun fetch(): FetchResult { + override suspend fun fetch(): FetchResult? { val repo = mangaRepositoryFactory.create(page.source) val pageUrl = repo.getPageUrl(page) if (options.diskCachePolicy.readEnabled) { pagesCache.get(pageUrl)?.let { file -> - return SourceResult( - source = ImageSource( - file = file.toOkioPath(), - metadata = MangaPageMetadata(page), - ), - mimeType = null, + return SourceFetchResult( + source = ImageSource(file.toOkioPath(), options.fileSystem), + mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(file.extension), dataSource = DataSource.DISK, ) } @@ -62,67 +55,50 @@ class MangaPageFetcher( return loadPage(pageUrl) } - private suspend fun loadPage(pageUrl: String): SourceResult { - val uri = pageUrl.toUri() - return when { - uri.isZipUri() -> runInterruptible(Dispatchers.IO) { - val zip = ZipFile(uri.schemeSpecificPart) - try { - val entry = zip.getEntry(uri.fragment) - SourceResult( - source = ImageSource( - source = zip.getInputStream(entry).source().withExtraCloseable(zip).buffer(), - context = context, - metadata = MangaPageMetadata(page), - ), - mimeType = MimeTypeMap.getSingleton() - .getMimeTypeFromExtension(entry.name.substringAfterLast('.', "")), - dataSource = DataSource.DISK, - ) - } catch (e: Throwable) { - zip.closeQuietly() - throw e - } - } + private suspend fun loadPage(pageUrl: String): FetchResult? = if (pageUrl.toUri().isNetworkUri()) { + fetchPage(pageUrl) + } else { + imageLoader.fetch(pageUrl, options) + } - uri.isFileUri() -> runInterruptible(Dispatchers.IO) { - val file = uri.toFile() - SourceResult( - source = ImageSource( - source = file.source().buffer(), - context = context, - metadata = MangaPageMetadata(page), - ), - mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(file.extension), - dataSource = DataSource.DISK, - ) + private suspend fun fetchPage(pageUrl: String): FetchResult { + val request = PageLoader.createPageRequest(pageUrl, page.source) + return imageProxyInterceptor.interceptPageRequest(request, okHttpClient).use { response -> + if (!response.isSuccessful) { + throw HttpException(response.toNetworkResponse()) } - - else -> { - val request = PageLoader.createPageRequest(pageUrl, page.source) - imageProxyInterceptor.interceptPageRequest(request, okHttpClient).use { response -> - if (!response.isSuccessful) { - throw HttpException(response) - } - val mimeType = response.mimeType - val file = response.requireBody().use { - pagesCache.put(pageUrl, it.source(), mimeType) - } - SourceResult( - source = ImageSource( - file = file.toOkioPath(), - metadata = MangaPageMetadata(page), - ), - mimeType = mimeType, - dataSource = DataSource.NETWORK, - ) - } + val mimeType = response.mimeType + val file = response.requireBody().use { + pagesCache.put(pageUrl, it.source(), mimeType) } + SourceFetchResult( + source = ImageSource(file.toOkioPath(), FileSystem.SYSTEM), + mimeType = mimeType, + dataSource = DataSource.NETWORK, + ) } } + private fun Response.toNetworkResponse(): NetworkResponse { + return NetworkResponse( + code = code, + requestMillis = sentRequestAtMillis, + responseMillis = receivedResponseAtMillis, + headers = headers.toNetworkHeaders(), + body = body?.source()?.let(::NetworkResponseBody), + delegate = this, + ) + } + + private fun Headers.toNetworkHeaders(): NetworkHeaders { + val headers = NetworkHeaders.Builder() + for ((key, values) in this) { + headers.add(key, values) + } + return headers.build() + } + class Factory @Inject constructor( - @ApplicationContext private val context: Context, @MangaHttpClient private val okHttpClient: OkHttpClient, private val pagesCache: PagesCache, private val mangaRepositoryFactory: MangaRepository.Factory, @@ -134,11 +110,9 @@ class MangaPageFetcher( pagesCache = pagesCache, options = options, page = data, - context = context, mangaRepositoryFactory = mangaRepositoryFactory, imageProxyInterceptor = imageProxyInterceptor, + imageLoader = imageLoader, ) } - - class MangaPageMetadata(val page: MangaPage) : ImageSource.Metadata() } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageKeyer.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageKeyer.kt index 6895c6ad8..8f137beed 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageKeyer.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/MangaPageKeyer.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.details.ui.pager.pages -import coil.key.Keyer -import coil.request.Options +import coil3.key.Keyer +import coil3.request.Options import org.koitharu.kotatsu.parsers.model.MangaPage class MangaPageKeyer : Keyer { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PageThumbnailAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PageThumbnailAD.kt index 832a6df67..494cbfdd7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PageThumbnailAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PageThumbnailAD.kt @@ -1,9 +1,10 @@ package org.koitharu.kotatsu.details.ui.pager.pages import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader -import coil.size.Scale -import coil.size.Size +import coil3.ImageLoader +import coil3.request.allowRgb565 +import coil3.size.Scale +import coil3.size.Size import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter @@ -11,9 +12,9 @@ import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.decodeRegion import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.setTextColorAttr -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemPageThumbBinding import org.koitharu.kotatsu.list.ui.model.ListModel import com.google.android.material.R as materialR @@ -42,13 +43,13 @@ fun pageThumbnailAD( scale(Scale.FILL) allowRgb565(true) decodeRegion(0) - source(item.page.source) + mangaSourceExtra(item.page.source) enqueueWith(coil) } with(binding.textViewNumber) { setBackgroundResource(if (item.isCurrent) R.drawable.bg_badge_accent else R.drawable.bg_badge_empty) setTextColorAttr(if (item.isCurrent) materialR.attr.colorOnTertiary else android.R.attr.textColorPrimary) - text = (item.number).toString() + text = item.number.toString() } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PageThumbnailAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PageThumbnailAdapter.kt index 5c66f2d7c..923c7776b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PageThumbnailAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PageThumbnailAdapter.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.details.ui.pager.pages import android.content.Context import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.list.fastscroll.FastScroller diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PagesFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PagesFragment.kt index 9ed57da9e..0edc18907 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PagesFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/pages/PagesFragment.kt @@ -10,7 +10,7 @@ import androidx.core.view.isVisible import androidx.fragment.app.viewModels import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt index 83326baff..6b629ec8d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.details.ui.scrobbling import androidx.fragment.app.FragmentManager import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoSheet.kt index de96a0a5a..69b25e63e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoSheet.kt @@ -14,7 +14,7 @@ import androidx.core.net.toUri import androidx.core.text.method.LinkMovementMethodCompat import androidx.fragment.app.FragmentManager import androidx.fragment.app.activityViewModels -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrollingInfoAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrollingInfoAdapter.kt index aa36cb8c1..fe205632a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrollingInfoAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrollingInfoAdapter.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.details.ui.scrobbling import androidx.fragment.app.FragmentManager import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.list.ui.model.ListModel diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemAD.kt index ec7e77736..0b3175086 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemAD.kt @@ -7,9 +7,11 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.work.WorkInfo -import coil.ImageLoader -import coil.request.SuccessResult -import coil.util.CoilUtils +import coil3.ImageLoader +import coil3.request.SuccessResult +import coil3.request.allowRgb565 +import coil3.request.transformations +import coil3.util.CoilUtils import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Job @@ -19,8 +21,8 @@ import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.image.TrimTransformation import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.ItemDownloadBinding import org.koitharu.kotatsu.download.ui.list.chapters.DownloadChapter @@ -92,7 +94,7 @@ fun downloadItemAD( allowRgb565(true) transformations(TrimTransformation()) memoryCacheKey(item.coverCacheKey) - source(item.manga?.source) + mangaSourceExtra(item.manga?.source) enqueueWith(coil) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemModel.kt index e783abf92..44b5c7f2d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemModel.kt @@ -7,7 +7,7 @@ import androidx.core.text.bold import androidx.core.text.buildSpannedString import androidx.core.text.color import androidx.work.WorkInfo -import coil.memory.MemoryCache +import coil3.memory.MemoryCache import kotlinx.coroutines.flow.StateFlow import org.koitharu.kotatsu.core.util.ext.getThemeColor import org.koitharu.kotatsu.download.ui.list.chapters.DownloadChapter diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt index 65cd369b9..0c823471e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt @@ -9,7 +9,7 @@ import androidx.activity.viewModels import androidx.appcompat.view.ActionMode import androidx.core.graphics.Insets import androidx.core.view.updatePadding -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.BaseActivity diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsAdapter.kt index ea30e09f7..46db5de9c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsAdapter.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.download.ui.list import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.list.ui.adapter.ListItemType import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt index ece4835a5..016bb14f2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt @@ -12,9 +12,10 @@ import androidx.core.app.NotificationManagerCompat import androidx.core.app.PendingIntentCompat import androidx.core.graphics.drawable.toBitmap import androidx.work.WorkManager -import coil.ImageLoader -import coil.request.ImageRequest -import coil.size.Scale +import coil3.ImageLoader +import coil3.request.ImageRequest +import coil3.request.allowHardware +import coil3.size.Scale import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -26,6 +27,7 @@ import org.koitharu.kotatsu.core.ErrorReporterReceiver import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow import org.koitharu.kotatsu.core.util.ext.isReportable +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.download.domain.DownloadState @@ -279,7 +281,7 @@ class DownloadNotificationFactory @AssistedInject constructor( ImageRequest.Builder(context) .data(manga.coverUrl) .allowHardware(false) - .tag(manga.source) + .mangaSourceExtra(manga.source) .size(coverWidth, coverHeight) .scale(Scale.FILL) .build(), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt index ac44169a7..de68d0922 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt @@ -18,7 +18,7 @@ import androidx.fragment.app.viewModels import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.ui.AllBookmarksActivity diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapter.kt index f275ff345..f8268614d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapter.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.explore.ui.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.explore.ui.model.MangaSourceItem diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt index 72cb24a6e..3f51907ca 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/adapter/ExploreAdapterDelegates.kt @@ -3,7 +3,12 @@ package org.koitharu.kotatsu.explore.ui.adapter import android.view.View import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 +import coil3.request.error +import coil3.request.fallback +import coil3.request.placeholder +import coil3.request.transformations import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.getSummary @@ -18,10 +23,10 @@ import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.drawableStart import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.recyclerView import org.koitharu.kotatsu.core.util.ext.setProgressIcon -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.ItemExploreButtonsBinding import org.koitharu.kotatsu.databinding.ItemExploreSourceGridBinding @@ -94,7 +99,7 @@ fun recommendationMangaItemAD( defaultPlaceholders(context) allowRgb565(true) transformations(TrimTransformation()) - source(item.manga.source) + mangaSourceExtra(item.manga.source) enqueueWith(coil) } } @@ -128,7 +133,7 @@ fun exploreSourceListItemAD( fallback(fallbackIcon) placeholder(AnimatedFaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)) error(fallbackIcon) - source(item.source) + mangaSourceExtra(item.source) enqueueWith(coil) } } @@ -160,7 +165,7 @@ fun exploreSourceGridItemAD( fallback(fallbackIcon) placeholder(AnimatedFaviconDrawable(context, R.style.FaviconDrawable_Large, item.source.name)) error(fallbackIcon) - source(item.source) + mangaSourceExtra(item.source) enqueueWith(coil) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt index f5129ba9a..b9073cf47 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt @@ -10,7 +10,7 @@ import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoriesAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoriesAdapter.kt index 8569744cd..67a5484f6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoriesAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoriesAdapter.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.favourites.ui.categories.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.ReorderableListAdapter import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesListListener import org.koitharu.kotatsu.list.ui.adapter.ListItemType diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt index 643aec708..ea4e032dd 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt @@ -14,14 +14,19 @@ import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.widget.ImageViewCompat import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 +import coil3.request.crossfade +import coil3.request.error +import coil3.request.fallback +import coil3.request.placeholder import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.getAnimationDuration import org.koitharu.kotatsu.core.util.ext.getThemeColor +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemCategoriesAllBinding import org.koitharu.kotatsu.databinding.ItemCategoryBinding import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesListListener @@ -87,7 +92,7 @@ fun categoryAD( coverViews[i].newImageRequest(lifecycleOwner, cover?.url)?.run { placeholder(R.drawable.ic_placeholder) fallback(fallback) - source(cover?.mangaSource) + mangaSourceExtra(cover?.mangaSource) crossfade(crossFadeDuration * (i + 1)) error(R.drawable.ic_error_placeholder) allowRgb565(true) @@ -153,7 +158,7 @@ fun allCategoriesAD( coverViews[i].newImageRequest(lifecycleOwner, cover?.url)?.run { placeholder(R.drawable.ic_placeholder) fallback(fallback) - source(cover?.mangaSource) + mangaSourceExtra(cover?.mangaSource) crossfade(crossFadeDuration * (i + 1)) error(R.drawable.ic_error_placeholder) allowRgb565(true) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt index a05651ab8..df99011ab 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt @@ -7,7 +7,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/CategoriesHeaderAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/CategoriesHeaderAD.kt index 28039b358..6f8fc332b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/CategoriesHeaderAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/CategoriesHeaderAD.kt @@ -9,7 +9,12 @@ import androidx.core.graphics.ColorUtils import androidx.core.view.isVisible import androidx.core.widget.ImageViewCompat import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 +import coil3.request.crossfade +import coil3.request.error +import coil3.request.fallback +import coil3.request.placeholder import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.util.ext.disposeImageRequest @@ -17,8 +22,8 @@ import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.getAnimationDuration import org.koitharu.kotatsu.core.util.ext.getThemeColor import org.koitharu.kotatsu.core.util.ext.joinToStringWithLimit +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemCategoriesHeaderBinding import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity @@ -74,7 +79,7 @@ fun categoriesHeaderAD( view.newImageRequest(lifecycleOwner, cover.url)?.run { placeholder(R.drawable.ic_placeholder) fallback(fallback) - source(cover.mangaSource) + mangaSourceExtra(cover.mangaSource) crossfade(crossFadeDuration * (i + 1)) error(R.drawable.ic_error_placeholder) allowRgb565(true) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoriesAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoriesAdapter.kt index dcdc92844..f820d6994 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoriesAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoriesAdapter.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.favourites.ui.categories.select.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerFragment.kt index bed90f277..217a4edb9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/container/FavouritesContainerFragment.kt @@ -12,7 +12,7 @@ import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.fragment.app.viewModels -import coil.ImageLoader +import coil3.ImageLoader import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListAdapter.kt index 426d5754e..64fe0b93e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListAdapter.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.history.ui import android.content.Context import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.list.fastscroll.FastScroller import org.koitharu.kotatsu.list.ui.adapter.MangaListAdapter import org.koitharu.kotatsu.list.ui.adapter.MangaListListener diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt index a8e7440e1..20f09ba8d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt @@ -13,12 +13,15 @@ import androidx.core.graphics.drawable.toBitmap import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.swiperefreshlayout.widget.CircularProgressDrawable -import coil.ImageLoader -import coil.request.CachePolicy -import coil.request.ErrorResult -import coil.request.ImageRequest -import coil.request.SuccessResult -import coil.target.ViewTarget +import coil3.Image +import coil3.ImageLoader +import coil3.asDrawable +import coil3.request.CachePolicy +import coil3.request.ErrorResult +import coil3.request.ImageRequest +import coil3.request.SuccessResult +import coil3.request.lifecycle +import coil3.target.ViewTarget import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.snackbar.Snackbar @@ -33,9 +36,9 @@ import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.getDisplayIcon import org.koitharu.kotatsu.core.util.ext.getDisplayMessage import org.koitharu.kotatsu.core.util.ext.getThemeColor +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ActivityImageBinding import org.koitharu.kotatsu.databinding.ItemErrorStateBinding import org.koitharu.kotatsu.parsers.model.MangaSource @@ -120,7 +123,7 @@ class ImageActivity : BaseActivity(), ImageRequest.Listene .memoryCachePolicy(CachePolicy.DISABLED) .lifecycle(this) .listener(this) - .source(MangaSource(intent.getStringExtra(EXTRA_SOURCE))) + .mangaSourceExtra(MangaSource(intent.getStringExtra(EXTRA_SOURCE))) .target(SsivTarget(viewBinding.ssiv)) .enqueueWith(coil) } @@ -152,9 +155,9 @@ class ImageActivity : BaseActivity(), ImageRequest.Listene override val view: SubsamplingScaleImageView, ) : ViewTarget { - override fun onError(error: Drawable?) = setDrawable(error) + override fun onError(error: Image?) = setDrawable(error?.asDrawable(view.resources)) - override fun onSuccess(result: Drawable) = setDrawable(result) + override fun onSuccess(result: Image) = setDrawable(result.asDrawable(view.resources)) override fun equals(other: Any?): Boolean { return (this === other) || (other is SsivTarget && view == other.view) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageMenuProvider.kt index 73ddf3a93..e190c9def 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageMenuProvider.kt @@ -11,8 +11,8 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.core.view.MenuProvider import com.google.android.material.snackbar.Snackbar import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.util.ext.isZipUri import org.koitharu.kotatsu.core.util.ext.tryLaunch -import org.koitharu.kotatsu.local.data.isZipUri class ImageMenuProvider( private val activity: ComponentActivity, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageViewModel.kt index cde609d67..8839a6416 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageViewModel.kt @@ -5,9 +5,9 @@ import android.graphics.Bitmap import android.net.Uri import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.SavedStateHandle -import coil.ImageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest +import coil3.ImageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers @@ -18,8 +18,8 @@ import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.require -import org.koitharu.kotatsu.core.util.ext.source import javax.inject.Inject @HiltViewModel @@ -37,7 +37,7 @@ class ImageViewModel @Inject constructor( .memoryCachePolicy(CachePolicy.READ_ONLY) .data(savedStateHandle.require(BaseActivity.EXTRA_DATA)) .memoryCachePolicy(CachePolicy.DISABLED) - .source(MangaSource(savedStateHandle[ImageActivity.EXTRA_SOURCE])) + .mangaSourceExtra(MangaSource(savedStateHandle[ImageActivity.EXTRA_SOURCE])) .build() val bitmap = coil.execute(request).getDrawableOrThrow().toBitmap() runInterruptible(Dispatchers.IO) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 46e5ae20a..0cf0a0bc9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -16,7 +16,7 @@ import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.recyclerview.widget.GridLayoutManager import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import org.koitharu.kotatsu.R diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyHintAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyHintAD.kt index 3aef252ef..96905bd07 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyHintAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyHintAD.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.list.ui.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.newImageRequest diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyStateListAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyStateListAD.kt index 25e0f57f9..76fa9f0d7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyStateListAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyStateListAD.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.list.ui.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.newImageRequest diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt index 97fa7b00e..395b294f1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt @@ -2,7 +2,9 @@ package org.koitharu.kotatsu.list.ui.adapter import androidx.core.view.isVisible import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 +import coil3.request.transformations import com.google.android.material.badge.BadgeDrawable import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver @@ -11,8 +13,8 @@ import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemMangaGridBinding import org.koitharu.kotatsu.list.ui.ListModelDiffCallback.Companion.PAYLOAD_PROGRESS_CHANGED import org.koitharu.kotatsu.list.ui.model.ListModel @@ -42,8 +44,7 @@ fun mangaGridItemAD( defaultPlaceholders(context) transformations(TrimTransformation()) allowRgb565(true) - tag(item.manga) - source(item.source) + mangaExtra(item.manga) enqueueWith(coil) } badge = itemView.bindBadge(badge, item.counter) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt index dd0a5767e..d683521e4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.list.ui.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.size.ItemSizeResolver diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt index 272235c05..f56a8916f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt @@ -1,7 +1,9 @@ package org.koitharu.kotatsu.list.ui.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 +import coil3.request.transformations import com.google.android.material.badge.BadgeDrawable import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver @@ -9,8 +11,8 @@ import org.koitharu.kotatsu.core.ui.image.TrimTransformation import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.ItemMangaListDetailsBinding import org.koitharu.kotatsu.list.ui.ListModelDiffCallback @@ -40,8 +42,7 @@ fun mangaListDetailedItemAD( defaultPlaceholders(context) transformations(TrimTransformation()) allowRgb565(true) - tag(item.manga) - source(item.source) + mangaExtra(item.manga) enqueueWith(coil) } binding.textViewTags.text = item.tags.joinToString(separator = ", ") { it.title ?: "" } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt index 14bf7f70b..a11da7f82 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt @@ -1,7 +1,9 @@ package org.koitharu.kotatsu.list.ui.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 +import coil3.request.transformations import com.google.android.material.badge.BadgeDrawable import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.core.ui.image.TrimTransformation @@ -9,8 +11,8 @@ import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.ItemMangaListBinding import org.koitharu.kotatsu.list.ui.model.ListModel @@ -35,8 +37,7 @@ fun mangaListItemAD( defaultPlaceholders(context) allowRgb565(true) transformations(TrimTransformation()) - tag(item.manga) - source(item.source) + mangaExtra(item.manga) enqueueWith(coil) } badge = itemView.bindBadge(badge, item.counter) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt index 132f69382..549544996 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/preview/PreviewFragment.kt @@ -9,10 +9,15 @@ import androidx.core.graphics.Insets import androidx.core.text.method.LinkMovementMethodCompat import androidx.core.view.isVisible import androidx.fragment.app.viewModels -import coil.ImageLoader -import coil.request.ImageRequest -import coil.request.SuccessResult -import coil.util.CoilUtils +import coil3.ImageLoader +import coil3.request.ImageRequest +import coil3.request.SuccessResult +import coil3.request.error +import coil3.request.fallback +import coil3.request.lifecycle +import coil3.request.placeholder +import coil3.request.target +import coil3.util.CoilUtils import com.google.android.material.chip.Chip import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R @@ -21,8 +26,10 @@ import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.core.util.ext.crossfade import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders +import org.koitharu.kotatsu.core.util.ext.drawable import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf import org.koitharu.kotatsu.core.util.ext.textAndVisible @@ -169,7 +176,7 @@ class PreviewFragment : BaseFragment(), View.OnClickList .target(requireViewBinding().imageViewCover) .size(CoverSizeResolver(requireViewBinding().imageViewCover)) .data(imageUrl) - .tag(manga.source) + .mangaSourceExtra(manga.source) .crossfade(requireContext()) .lifecycle(viewLifecycleOwner) .placeholderMemoryCacheKey(manga.coverUrl) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFetcher.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFetcher.kt deleted file mode 100644 index c80fefa1e..000000000 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFetcher.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.koitharu.kotatsu.local.data - -import android.net.Uri -import android.webkit.MimeTypeMap -import coil.ImageLoader -import coil.decode.DataSource -import coil.decode.ImageSource -import coil.fetch.Fetcher -import coil.fetch.SourceResult -import coil.request.Options -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runInterruptible -import okhttp3.internal.closeQuietly -import okio.buffer -import okio.source -import org.koitharu.kotatsu.local.data.util.withExtraCloseable -import java.util.zip.ZipFile - -class CbzFetcher( - private val uri: Uri, - private val options: Options -) : Fetcher { - - override suspend fun fetch() = runInterruptible(Dispatchers.IO) { - val zip = ZipFile(uri.schemeSpecificPart) - try { - val entry = zip.getEntry(uri.fragment) - val ext = MimeTypeMap.getFileExtensionFromUrl(entry.name) - val bufferedSource = zip.getInputStream(entry).source().withExtraCloseable(zip).buffer() - SourceResult( - source = ImageSource( - source = bufferedSource, - context = options.context, - metadata = CbzMetadata(uri), - ), - mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext), - dataSource = DataSource.DISK, - ) - } catch (e: Throwable) { - zip.closeQuietly() - throw e - } - } - - class Factory : Fetcher.Factory { - - 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() -} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFilter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFilter.kt index 3539e8ca7..0000dc536 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFilter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/CbzFilter.kt @@ -1,7 +1,5 @@ package org.koitharu.kotatsu.local.data -import android.net.Uri -import org.koitharu.kotatsu.core.util.ext.URI_SCHEME_ZIP import java.io.File private fun isCbzExtension(ext: String?): Boolean { @@ -14,9 +12,3 @@ fun hasCbzExtension(string: String): Boolean { } fun File.hasCbzExtension() = isCbzExtension(extension) - -fun Uri.isZipUri() = scheme.let { - it == URI_SCHEME_ZIP || it == "cbz" || it == "zip" -} - -fun Uri.isFileUri() = scheme == "file" diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalStorageManager.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalStorageManager.kt index be36c273e..cb14266e6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalStorageManager.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalStorageManager.kt @@ -19,9 +19,9 @@ import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.withContext import okhttp3.Cache import org.koitharu.kotatsu.core.prefs.AppSettings -import org.koitharu.kotatsu.core.util.ext.URI_SCHEME_FILE import org.koitharu.kotatsu.core.util.ext.computeSize import org.koitharu.kotatsu.core.util.ext.getStorageName +import org.koitharu.kotatsu.core.util.ext.isFileUri import org.koitharu.kotatsu.core.util.ext.resolveFile import org.koitharu.kotatsu.parsers.util.mapToSet import java.io.File @@ -90,7 +90,7 @@ class LocalStorageManager @Inject constructor( } suspend fun resolveUri(uri: Uri): File? = runInterruptible(Dispatchers.IO) { - if (uri.scheme == URI_SCHEME_FILE) { + if (uri.isFileUri()) { uri.toFile() } else { uri.resolveFile(context) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaInput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaInput.kt index 13d129235..7b01c469f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaInput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaInput.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch +import org.koitharu.kotatsu.core.util.ext.toZipUri import org.koitharu.kotatsu.local.data.hasCbzExtension import org.koitharu.kotatsu.local.domain.model.LocalManga import org.koitharu.kotatsu.parsers.model.Manga @@ -64,8 +65,7 @@ sealed class LocalMangaInput( }.flowOn(Dispatchers.Default).firstOrNull() @JvmStatic - protected fun zipUri(file: File, entryName: String): String = - Uri.fromParts("cbz", file.path, entryName).toString() + protected fun zipUri(file: File, entryName: String): String = file.toZipUri(entryName).toString() @JvmStatic protected fun Manga.copy2( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/util/ExtraCloseableSource.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/util/ExtraCloseableSource.kt deleted file mode 100644 index b83867e5c..000000000 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/util/ExtraCloseableSource.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.koitharu.kotatsu.local.data.util - -import okhttp3.internal.closeQuietly -import okio.Closeable -import okio.Source - -private class ExtraCloseableSource( - private val delegate: Source, - private val extraCloseable: Closeable, -) : Source by delegate { - - override fun close() { - try { - delegate.close() - } finally { - extraCloseable.closeQuietly() - } - } -} - -fun Source.withExtraCloseable(closeable: Closeable): Source = ExtraCloseableSource(this, closeable) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportService.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportService.kt index 8ff8ca534..bdf47e8ab 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportService.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/ImportService.kt @@ -13,8 +13,8 @@ import androidx.core.app.NotificationManagerCompat import androidx.core.app.PendingIntentCompat import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat -import coil.ImageLoader -import coil.request.ImageRequest +import coil3.ImageLoader +import coil3.request.ImageRequest import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.runBlocking import org.koitharu.kotatsu.R @@ -22,6 +22,7 @@ import org.koitharu.kotatsu.core.ErrorReporterReceiver import org.koitharu.kotatsu.core.ui.CoroutineIntentService import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission import org.koitharu.kotatsu.core.util.ext.getDisplayMessage +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull import org.koitharu.kotatsu.core.util.ext.toUriOrNull @@ -113,7 +114,7 @@ class ImportService : CoroutineIntentService() { coil.execute( ImageRequest.Builder(applicationContext) .data(manga.coverUrl) - .tag(manga.source) + .mangaSourceExtra(manga.source) .build(), ).toBitmapOrNull(), ) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/CoverRestoreInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/CoverRestoreInterceptor.kt index 9f27b6147..73cc2cb81 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/CoverRestoreInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/CoverRestoreInterceptor.kt @@ -1,10 +1,10 @@ package org.koitharu.kotatsu.main.domain import androidx.collection.ArraySet -import coil.intercept.Interceptor -import coil.network.HttpException -import coil.request.ErrorResult -import coil.request.ImageResult +import coil3.intercept.Interceptor +import coil3.network.HttpException +import coil3.request.ErrorResult +import coil3.request.ImageResult import okio.FileNotFoundException import org.jsoup.HttpStatusException import org.koitharu.kotatsu.bookmarks.domain.Bookmark @@ -13,7 +13,9 @@ import org.koitharu.kotatsu.core.model.findById import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.parser.MangaDataRepository import org.koitharu.kotatsu.core.parser.MangaRepository +import org.koitharu.kotatsu.core.util.ext.bookmarkKey import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty +import org.koitharu.kotatsu.core.util.ext.mangaKey import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.model.Manga @@ -33,18 +35,18 @@ class CoverRestoreInterceptor @Inject constructor( override suspend fun intercept(chain: Interceptor.Chain): ImageResult { val request = chain.request - val result = chain.proceed(request) + val result = chain.proceed() if (result is ErrorResult && result.throwable.shouldRestore()) { - request.tags.tag()?.let { + request.extras[mangaKey]?.let { if (restoreManga(it)) { - return chain.proceed(request.newBuilder().build()) + return chain.withRequest(request.newBuilder().build()).proceed() } else { return result } } - request.tags.tag()?.let { + request.extras[bookmarkKey]?.let { if (restoreBookmark(it)) { - return chain.proceed(request.newBuilder().build()) + return chain.withRequest(request.newBuilder().build()).proceed() } else { return result } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/DetectReaderModeUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/DetectReaderModeUseCase.kt index 9870d4d9c..2eceea6d5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/DetectReaderModeUseCase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/DetectReaderModeUseCase.kt @@ -14,9 +14,9 @@ import org.koitharu.kotatsu.core.parser.MangaDataRepository import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ReaderMode +import org.koitharu.kotatsu.core.util.ext.isFileUri +import org.koitharu.kotatsu.core.util.ext.isZipUri import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug -import org.koitharu.kotatsu.local.data.isFileUri -import org.koitharu.kotatsu.local.data.isZipUri import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.util.runCatchingCancellable diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt index d259ea6fc..9de3dffdb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/domain/PageLoader.kt @@ -1,10 +1,8 @@ package org.koitharu.kotatsu.reader.domain -import android.content.ContentResolver.MimeTypeInfo import android.content.Context import android.graphics.Rect import android.net.Uri -import android.webkit.MimeTypeMap import androidx.annotation.AnyThread import androidx.collection.LongSparseArray import androidx.collection.set @@ -29,6 +27,8 @@ import kotlinx.coroutines.sync.withPermit import okhttp3.OkHttpClient import okhttp3.Request import okio.use +import org.jetbrains.annotations.Blocking +import org.koitharu.kotatsu.core.image.BitmapDecoderCompat import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.network.MangaHttpClient import org.koitharu.kotatsu.core.network.imageproxy.ImageProxyInterceptor @@ -42,26 +42,25 @@ import org.koitharu.kotatsu.core.util.ext.cancelChildrenAndJoin import org.koitharu.kotatsu.core.util.ext.compressToPNG import org.koitharu.kotatsu.core.util.ext.ensureRamAtLeast import org.koitharu.kotatsu.core.util.ext.ensureSuccess -import org.koitharu.kotatsu.core.util.ext.exists import org.koitharu.kotatsu.core.util.ext.getCompletionResultOrNull +import org.koitharu.kotatsu.core.util.ext.isFileUri +import org.koitharu.kotatsu.core.util.ext.isNotEmpty import org.koitharu.kotatsu.core.util.ext.isPowerSaveMode -import org.koitharu.kotatsu.core.util.ext.isTargetNotEmpty +import org.koitharu.kotatsu.core.util.ext.isZipUri +import org.koitharu.kotatsu.core.util.ext.mimeType import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.ramAvailable import org.koitharu.kotatsu.core.util.ext.use import org.koitharu.kotatsu.core.util.ext.withProgress import org.koitharu.kotatsu.core.util.progress.ProgressDeferred import org.koitharu.kotatsu.local.data.PagesCache -import org.koitharu.kotatsu.local.data.isFileUri -import org.koitharu.kotatsu.local.data.isZipUri import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.mimeType import org.koitharu.kotatsu.parsers.util.requireBody import org.koitharu.kotatsu.parsers.util.runCatchingCancellable -import org.koitharu.kotatsu.core.image.BitmapDecoderCompat -import org.koitharu.kotatsu.core.util.ext.mimeType import org.koitharu.kotatsu.reader.ui.pager.ReaderPage +import java.io.File import java.util.LinkedList import java.util.concurrent.atomic.AtomicInteger import java.util.zip.ZipFile @@ -276,5 +275,28 @@ class PageLoader @Inject constructor( .cacheControl(CommonHeaders.CACHE_CONTROL_NO_STORE) .tag(MangaSource::class.java, mangaSource) .build() + + + @Blocking + private fun Uri.exists(): Boolean = when { + isFileUri() -> toFile().exists() + isZipUri() -> { + val file = File(requireNotNull(schemeSpecificPart)) + file.exists() && ZipFile(file).use { it.getEntry(fragment) != null } + } + + else -> false + } + + @Blocking + private fun Uri.isTargetNotEmpty(): Boolean = when { + isFileUri() -> toFile().isNotEmpty() + isZipUri() -> { + val file = File(requireNotNull(schemeSpecificPart)) + file.exists() && ZipFile(file).use { (it.getEntry(fragment)?.size ?: 0L) != 0L } + } + + else -> false + } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/PageSaveHelper.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/PageSaveHelper.kt index 9a8f4b43d..2a0e7732f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/PageSaveHelper.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/PageSaveHelper.kt @@ -5,6 +5,7 @@ import android.graphics.BitmapFactory import android.net.Uri import android.webkit.MimeTypeMap import androidx.activity.result.ActivityResultLauncher +import androidx.core.net.toFile import androidx.core.net.toUri import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers @@ -12,11 +13,17 @@ import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext import okhttp3.HttpUrl.Companion.toHttpUrl +import okio.FileSystem import okio.IOException +import okio.Path.Companion.toPath +import okio.Source import okio.buffer +import okio.openZip import okio.sink +import okio.source import org.koitharu.kotatsu.core.prefs.AppSettings -import org.koitharu.kotatsu.core.util.ext.source +import org.koitharu.kotatsu.core.util.ext.isFileUri +import org.koitharu.kotatsu.core.util.ext.isZipUri import org.koitharu.kotatsu.core.util.ext.toFileOrNull import org.koitharu.kotatsu.core.util.ext.writeAllCancellable import org.koitharu.kotatsu.parsers.model.MangaPage @@ -50,7 +57,7 @@ class PageSaveHelper @Inject constructor( runInterruptible(Dispatchers.IO) { contentResolver.openOutputStream(destination)?.sink()?.buffer() }?.use { output -> - pageUri.source().use { input -> + getSource(pageUri).use { input -> output.writeAllCancellable(input) } } ?: throw IOException("Output stream is null") @@ -68,6 +75,14 @@ class PageSaveHelper @Inject constructor( } } + private fun getSource(uri: Uri): Source = when { + uri.isFileUri() -> uri.toFile().source() + uri.isZipUri() -> FileSystem.SYSTEM.openZip(uri.schemeSpecificPart.toPath()) + .source(requireNotNull(uri.fragment).toPath()) + + else -> throw IllegalArgumentException("Bad uri $uri: unsupported scheme") + } + private suspend fun pickFileUri(saveLauncher: ActivityResultLauncher, proposedName: String): Uri { val defaultUri = settings.getPagesSaveDir(context)?.uri?.buildUpon()?.appendPath(proposedName)?.toString() return withContext(Dispatchers.Main) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt index 8fb632c59..c9444532a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/ColorFilterConfigActivity.kt @@ -12,10 +12,12 @@ import androidx.activity.viewModels import androidx.core.graphics.Insets import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding -import coil.ImageLoader -import coil.request.ImageRequest -import coil.size.Scale -import coil.size.ViewSizeResolver +import coil3.ImageLoader +import coil3.request.ImageRequest +import coil3.request.bitmapConfig +import coil3.request.error +import coil3.size.Scale +import coil3.size.ViewSizeResolver import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.slider.LabelFormatter import com.google.android.material.slider.Slider @@ -27,6 +29,7 @@ import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.util.ext.decodeRegion import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.indicator +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.setChecked @@ -139,7 +142,7 @@ class ColorFilterConfigActivity : .data(data) .scale(Scale.FILL) .decodeRegion() - .tag(page.source) + .mangaSourceExtra(page.source) .bitmapConfig(if (viewModel.is32BitColorsEnabled) Bitmap.Config.ARGB_8888 else Bitmap.Config.RGB_565) .indicator(listOf(viewBinding.progressBefore, viewBinding.progressAfter)) .error(R.drawable.ic_error_placeholder) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/DoubleViewTarget.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/DoubleViewTarget.kt index 28bf4f38c..eeaf9e1ae 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/DoubleViewTarget.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/colorfilter/DoubleViewTarget.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.reader.ui.colorfilter import android.graphics.drawable.Drawable import android.widget.ImageView -import coil.target.ImageViewTarget +import coil3.target.ImageViewTarget class DoubleViewTarget( primaryView: ImageView, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt index 3f4d6bafa..a10a912f4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/ScrobblerConfigActivity.kt @@ -7,7 +7,10 @@ import android.view.View import androidx.activity.viewModels import androidx.core.graphics.Insets import androidx.core.view.updatePadding -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.error +import coil3.request.fallback +import coil3.request.placeholder import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAD.kt index 68050b01f..c27da88a8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAD.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.scrobbling.common.ui.config.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAdapter.kt index b43004a6d..04511f5cf 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/config/adapter/ScrobblingMangaAdapter.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.scrobbling.common.ui.config.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.list.ui.adapter.ListItemType diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorSheet.kt index 604602ae1..ab78e7d5a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorSheet.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorSheet.kt @@ -11,7 +11,7 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.RecyclerView.NO_ID -import coil.ImageLoader +import coil3.ImageLoader import com.google.android.material.tabs.TabLayout import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerSelectorAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerSelectorAdapter.kt index f8d230fd0..38acf30a2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerSelectorAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblerSelectorAdapter.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.list.ui.adapter.ListItemType diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblingMangaAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblingMangaAD.kt index 29d27adf1..2aca8cf01 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblingMangaAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/scrobbling/common/ui/selector/adapter/ScrobblingMangaAD.kt @@ -1,7 +1,8 @@ package org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt index 781dc822f..c100efbcc 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt @@ -11,7 +11,7 @@ import androidx.activity.viewModels import androidx.appcompat.view.ActionMode import androidx.core.graphics.Insets import androidx.core.view.updatePadding -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchAdapter.kt index 1d8e62937..d14206a68 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchAdapter.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.search.ui.multi.adapter import android.content.Context import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.RecyclerView.RecycledViewPool -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.list.fastscroll.FastScroller diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchResultsAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchResultsAD.kt index 64a45b8d8..f7c2c13f4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchResultsAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchResultsAD.kt @@ -4,7 +4,7 @@ import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.RecyclerView.RecycledViewPool -import coil.ImageLoader +import coil3.ImageLoader import com.hannesdorfmann.adapterdelegates4.ListDelegationAdapter import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt index b24f2d0df..49b7d467c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionFragment.kt @@ -7,7 +7,7 @@ import androidx.core.graphics.Insets import androidx.core.view.updatePadding import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.ItemTouchHelper -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.os.VoiceInputContract @@ -74,11 +74,12 @@ class SearchSuggestionFragment : companion object { - @Deprecated("", + @Deprecated( + "", ReplaceWith( "SearchSuggestionFragment()", - "org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionFragment" - ) + "org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionFragment", + ), ) fun newInstance() = SearchSuggestionFragment() } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionAdapter.kt index 9b923e571..a788c6b88 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionAdapter.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.search.ui.suggestion.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener import org.koitharu.kotatsu.search.ui.suggestion.model.SearchSuggestionItem diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt index 47415984d..8bb08321b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceAD.kt @@ -1,7 +1,10 @@ package org.koitharu.kotatsu.search.ui.suggestion.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.error +import coil3.request.fallback +import coil3.request.placeholder import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.getSummary @@ -10,8 +13,8 @@ import org.koitharu.kotatsu.core.parser.favicon.faviconUri import org.koitharu.kotatsu.core.ui.image.AnimatedFaviconDrawable import org.koitharu.kotatsu.core.ui.image.FaviconDrawable import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemSearchSuggestionSourceBinding import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener import org.koitharu.kotatsu.search.ui.suggestion.model.SearchSuggestionItem @@ -40,7 +43,7 @@ fun searchSuggestionSourceAD( fallback(fallbackIcon) placeholder(AnimatedFaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)) error(fallbackIcon) - source(item.source) + mangaSourceExtra(item.source) enqueueWith(coil) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceTipAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceTipAD.kt index 7dad1c854..7d9b07ab1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceTipAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionSourceTipAD.kt @@ -1,7 +1,10 @@ package org.koitharu.kotatsu.search.ui.suggestion.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.error +import coil3.request.fallback +import coil3.request.placeholder import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.getSummary @@ -10,8 +13,8 @@ import org.koitharu.kotatsu.core.parser.favicon.faviconUri import org.koitharu.kotatsu.core.ui.image.AnimatedFaviconDrawable import org.koitharu.kotatsu.core.ui.image.FaviconDrawable import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemSearchSuggestionSourceTipBinding import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener import org.koitharu.kotatsu.search.ui.suggestion.model.SearchSuggestionItem @@ -37,7 +40,7 @@ fun searchSuggestionSourceTipAD( fallback(fallbackIcon) placeholder(AnimatedFaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)) error(fallbackIcon) - source(item.source) + mangaSourceExtra(item.source) enqueueWith(coil) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt index 789704e90..3c58a75ac 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/adapter/SearchSuggestionsMangaListAD.kt @@ -4,7 +4,9 @@ import androidx.core.view.updatePadding import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 +import coil3.request.transformations import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding @@ -14,8 +16,8 @@ import org.koitharu.kotatsu.core.ui.list.decor.SpacingItemDecoration import org.koitharu.kotatsu.core.util.RecyclerViewScrollCallback import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemSearchSuggestionMangaGridBinding import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener @@ -61,7 +63,7 @@ private fun searchSuggestionMangaGridAD( defaultPlaceholders(context) allowRgb565(true) transformations(TrimTransformation()) - source(item.source) + mangaSourceExtra(item.source) enqueueWith(coil) } binding.textViewTitle.text = item.title diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapter.kt index a27037fa4..ae2ccf073 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapter.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.settings.sources.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.ReorderableListAdapter import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt index ca269f4a0..9d705714d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt @@ -7,7 +7,10 @@ import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.error +import coil3.request.fallback +import coil3.request.placeholder import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R @@ -20,8 +23,8 @@ import org.koitharu.kotatsu.core.ui.list.OnTipCloseListener import org.koitharu.kotatsu.core.util.ext.crossfade import org.koitharu.kotatsu.core.util.ext.drawableStart import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemSourceConfigBinding import org.koitharu.kotatsu.databinding.ItemTipBinding import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem @@ -65,7 +68,7 @@ fun sourceConfigItemDelegate2( error(fallbackIcon) placeholder(AnimatedFaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)) fallback(fallbackIcon) - source(item.source) + mangaSourceExtra(item.source) enqueueWith(coil) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItemAD.kt index 35c94f045..09fb7ab92 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItemAD.kt @@ -4,7 +4,10 @@ import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.core.view.updatePaddingRelative import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.error +import coil3.request.fallback +import coil3.request.placeholder import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.browser.cloudflare.CaptchaNotifier.Companion.ignoreCaptchaErrors @@ -18,9 +21,9 @@ import org.koitharu.kotatsu.core.util.ext.crossfade import org.koitharu.kotatsu.core.util.ext.drawableStart import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.getThemeDimensionPixelOffset +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.setTextAndVisible -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemEmptyHintBinding import org.koitharu.kotatsu.databinding.ItemSourceCatalogBinding import org.koitharu.kotatsu.list.ui.model.ListModel @@ -64,7 +67,7 @@ fun sourceCatalogItemSourceAD( error(fallbackIcon) placeholder(AnimatedFaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)) fallback(fallbackIcon) - source(item.source) + mangaSourceExtra(item.source) ignoreCaptchaErrors() enqueueWith(coil) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt index 543a4e707..bb4b71200 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt @@ -9,7 +9,7 @@ import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView import androidx.core.graphics.Insets import androidx.core.view.updatePadding -import coil.ImageLoader +import coil3.ImageLoader import com.google.android.material.appbar.AppBarLayout import com.google.android.material.chip.Chip import dagger.hilt.android.AndroidEntryPoint diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt index 0e2ebbf01..bb2e411e5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.settings.sources.catalog import android.content.Context import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt index d0b19015b..07bcc8921 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt @@ -14,7 +14,7 @@ import androidx.core.view.updatePadding import androidx.fragment.app.viewModels import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import org.koitharu.kotatsu.R diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt index fff0a64f0..141e9e487 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/stats/ui/StatsActivity.kt @@ -12,7 +12,7 @@ import androidx.core.graphics.Insets import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.recyclerview.widget.AsyncListDiffer -import coil.ImageLoader +import coil3.ImageLoader import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipDrawable import dagger.hilt.android.AndroidEntryPoint diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt index 172db74e7..d7bfd3b67 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsWorker.kt @@ -31,8 +31,8 @@ import androidx.work.WorkManager import androidx.work.WorkerParameters import androidx.work.await import androidx.work.workDataOf -import coil.ImageLoader -import coil.request.ImageRequest +import coil3.ImageLoader +import coil3.request.ImageRequest import dagger.Reusable import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -57,6 +57,7 @@ import org.koitharu.kotatsu.core.util.ext.awaitUniqueWorkInfoByName import org.koitharu.kotatsu.core.util.ext.awaitWorkInfosByTag import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission import org.koitharu.kotatsu.core.util.ext.flatten +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.sanitize import org.koitharu.kotatsu.core.util.ext.sizeOrZero @@ -257,7 +258,7 @@ class SuggestionsWorker @AssistedInject constructor( val list = repository.getList( offset = 0, order = order, - filter = MangaListFilter(tags = setOfNotNull(tag)) + filter = MangaListFilter(tags = setOfNotNull(tag)), ).asArrayList() if (appSettings.isSuggestionsExcludeNsfw) { list.removeAll { it.isNsfw } @@ -296,7 +297,7 @@ class SuggestionsWorker @AssistedInject constructor( coil.execute( ImageRequest.Builder(applicationContext) .data(manga.coverUrl) - .tag(manga.source) + .mangaSourceExtra(manga.source) .build(), ).toBitmapOrNull(), ) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/domain/CheckNewChaptersUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/domain/CheckNewChaptersUseCase.kt index 886e90a47..6802672d6 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/domain/CheckNewChaptersUseCase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/domain/CheckNewChaptersUseCase.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.tracker.domain -import coil.request.CachePolicy +import coil3.request.CachePolicy import org.koitharu.kotatsu.core.model.getPreferredBranch import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.parser.CachingMangaRepository diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackDebugAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackDebugAD.kt index e305e5f8e..9a9c589aa 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackDebugAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackDebugAD.kt @@ -7,7 +7,8 @@ import androidx.core.text.bold import androidx.core.text.buildSpannedString import androidx.core.text.color import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener @@ -15,8 +16,8 @@ import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.drawableStart import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.getThemeColor +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemTrackDebugBinding import org.koitharu.kotatsu.tracker.data.TrackEntity import com.google.android.material.R as materialR @@ -38,7 +39,7 @@ fun trackDebugAD( binding.imageViewCover.newImageRequest(lifecycleOwner, item.manga.coverUrl)?.run { defaultPlaceholders(context) allowRgb565(true) - source(item.manga.source) + mangaSourceExtra(item.manga.source) enqueueWith(coil) } binding.textViewTitle.text = item.manga.title diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackerDebugActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackerDebugActivity.kt index 517008984..18d02dd3e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackerDebugActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/debug/TrackerDebugActivity.kt @@ -5,7 +5,7 @@ import android.view.View import androidx.activity.viewModels import androidx.core.graphics.Insets import androidx.core.view.updatePadding -import coil.ImageLoader +import coil3.ImageLoader import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.BaseListAdapter diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt index 9ac349c60..6fb310599 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/FeedFragment.kt @@ -10,7 +10,7 @@ import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import coil.ImageLoader +import coil3.ImageLoader import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.drop diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedAdapter.kt index 4e212ad1f..d6c4951ff 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedAdapter.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.tracker.ui.feed.adapter import android.content.Context import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.list.fastscroll.FastScroller diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt index e69ceb513..0f67d0f1f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/FeedItemAD.kt @@ -2,15 +2,16 @@ package org.koitharu.kotatsu.tracker.ui.feed.adapter import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader +import coil3.request.allowRgb565 import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ext.defaultPlaceholders import org.koitharu.kotatsu.core.util.ext.drawableStart import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.newImageRequest -import org.koitharu.kotatsu.core.util.ext.source import org.koitharu.kotatsu.databinding.ItemFeedBinding import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.tracker.ui.feed.model.FeedItem @@ -32,7 +33,7 @@ fun feedItemAD( binding.imageViewCover.newImageRequest(lifecycleOwner, item.imageUrl)?.run { defaultPlaceholders(context) allowRgb565(true) - source(item.manga.source) + mangaSourceExtra(item.manga.source) enqueueWith(coil) } binding.textViewTitle.text = item.title diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/UpdatedMangaAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/UpdatedMangaAD.kt index cb30a3c1f..4d6db468d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/UpdatedMangaAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/feed/adapter/UpdatedMangaAD.kt @@ -1,7 +1,7 @@ package org.koitharu.kotatsu.tracker.ui.feed.adapter import androidx.lifecycle.LifecycleOwner -import coil.ImageLoader +import coil3.ImageLoader import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.BaseListAdapter diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackerNotificationHelper.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackerNotificationHelper.kt index 38b952072..dc18149a5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackerNotificationHelper.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/work/TrackerNotificationHelper.kt @@ -12,12 +12,13 @@ import androidx.core.app.NotificationCompat.VISIBILITY_SECRET import androidx.core.app.NotificationManagerCompat import androidx.core.app.PendingIntentCompat import androidx.core.content.ContextCompat -import coil.ImageLoader -import coil.request.ImageRequest +import coil3.ImageLoader +import coil3.request.ImageRequest import dagger.hilt.android.qualifiers.ApplicationContext import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.parsers.model.Manga @@ -67,7 +68,7 @@ class TrackerNotificationHelper @Inject constructor( coil.execute( ImageRequest.Builder(applicationContext) .data(manga.coverUrl) - .tag(manga.source) + .mangaSourceExtra(manga.source) .build(), ).toBitmapOrNull(), ) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt b/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt index 30d28b251..a4e047999 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentListFactory.kt @@ -5,20 +5,23 @@ import android.content.Intent import android.widget.RemoteViews import android.widget.RemoteViewsService import androidx.core.graphics.drawable.toBitmap -import coil.ImageLoader -import coil.executeBlocking -import coil.request.ImageRequest -import coil.size.Size -import coil.transform.RoundedCornersTransformation +import coil3.ImageLoader +import coil3.executeBlocking +import coil3.request.ImageRequest +import coil3.request.transformations +import coil3.size.Size +import coil3.transform.RoundedCornersTransformation import dagger.Lazy import kotlinx.coroutines.runBlocking import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.parser.MangaIntent import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow +import org.koitharu.kotatsu.core.util.ext.mangaExtra import org.koitharu.kotatsu.history.data.HistoryRepository import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.util.replaceWith +import org.koitharu.kotatsu.parsers.util.runCatchingCancellable class RecentListFactory( private val context: Context, @@ -56,13 +59,12 @@ class RecentListFactory( override fun getViewAt(position: Int): RemoteViews { val views = RemoteViews(context.packageName, R.layout.item_recent) val item = dataSet.getOrNull(position) ?: return views - runCatching { + runCatchingCancellable { coilLazy.get().executeBlocking( ImageRequest.Builder(context) .data(item.coverUrl) .size(coverSize) - .tag(item.source) - .tag(item) + .mangaExtra(item) .transformations(transformation) .build(), ).getDrawableOrThrow().toBitmap() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentWidgetService.kt b/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentWidgetService.kt index 3009db546..3f6455242 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentWidgetService.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/widget/recent/RecentWidgetService.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.widget.recent import android.content.Intent import android.widget.RemoteViewsService -import coil.ImageLoader +import coil3.ImageLoader import dagger.Lazy import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.core.prefs.AppSettings diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt b/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt index 3c74a4428..26d4cee95 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfListFactory.kt @@ -5,11 +5,12 @@ import android.content.Intent import android.widget.RemoteViews import android.widget.RemoteViewsService import androidx.core.graphics.drawable.toBitmap -import coil.ImageLoader -import coil.executeBlocking -import coil.request.ImageRequest -import coil.size.Size -import coil.transform.RoundedCornersTransformation +import coil3.ImageLoader +import coil3.executeBlocking +import coil3.request.ImageRequest +import coil3.request.transformations +import coil3.size.Size +import coil3.transform.RoundedCornersTransformation import dagger.Lazy import kotlinx.coroutines.runBlocking import org.koitharu.kotatsu.R @@ -18,6 +19,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppWidgetConfig import org.koitharu.kotatsu.core.ui.image.TrimTransformation import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow +import org.koitharu.kotatsu.core.util.ext.mangaExtra import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.util.replaceWith @@ -73,8 +75,7 @@ class ShelfListFactory( ImageRequest.Builder(context) .data(item.coverUrl) .size(coverSize) - .tag(item.source) - .tag(item) + .mangaExtra(item) .transformations(transformation, TrimTransformation()) .build(), ).getDrawableOrThrow().toBitmap() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfWidgetService.kt b/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfWidgetService.kt index b8fdffea3..9f9052f12 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfWidgetService.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/widget/shelf/ShelfWidgetService.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.widget.shelf import android.appwidget.AppWidgetManager import android.content.Intent import android.widget.RemoteViewsService -import coil.ImageLoader +import coil3.ImageLoader import dagger.Lazy import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.core.prefs.AppSettings diff --git a/build.gradle b/build.gradle index 48081cc88..88f6c8378 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.7.0' + classpath 'com.android.tools.build:gradle:8.7.1' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.20' classpath 'com.google.dagger:hilt-android-gradle-plugin:2.52' classpath 'com.google.devtools.ksp:symbol-processing-gradle-plugin:2.0.20-1.0.25' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e834d20c..0e345a501 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ #Wed Apr 03 08:23:55 EEST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists