From cce6fdb7653d96bf9f6f1bd48e4bb74f223a4671 Mon Sep 17 00:00:00 2001 From: Jay Date: Sat, 16 May 2020 23:54:43 -0400 Subject: [PATCH] Coil updates Non library manga goes into android's default cache Manga Fetcher now respects network cache policy Not showing error drawable on manga details Fixed manga notification icon sometimes not showing General cleanup --- .../kanade/tachiyomi/data/cache/CoverCache.kt | 3 - .../data/download/coil/ByteArrayFetcher.kt | 1 - .../tachiyomi/data/download/coil/CoilSetup.kt | 2 - .../data/download/coil/MangaFetcher.kt | 108 +++++++++++++----- .../data/library/LibraryUpdateService.kt | 15 ++- .../tachiyomi/ui/library/LibraryListHolder.kt | 7 +- .../ui/manga/MangaDetailsController.kt | 12 +- .../ui/manga/MangaDetailsPresenter.kt | 1 - .../tachiyomi/ui/manga/MangaHeaderHolder.kt | 21 ++-- .../source/browse/BrowseSourceGridHolder.kt | 5 +- .../source/browse/BrowseSourceListHolder.kt | 5 +- .../tachiyomi/widget/CoverViewTarget.kt | 12 +- 12 files changed, 118 insertions(+), 74 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt index 88d620165e..b884490805 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt @@ -13,7 +13,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import okhttp3.Cache import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.io.File @@ -36,8 +35,6 @@ class CoverCache(val context: Context) { private val cacheDir = context.getExternalFilesDir("covers") ?: File(context.filesDir, "covers").also { it.mkdirs() } - val cache = Cache(cacheDir, 300 * 1024 * 1024) // 300MB - fun getChapterCacheSize(): String { return Formatter.formatFileSize(context, DiskUtil.getDirectorySize(cacheDir)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/ByteArrayFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/ByteArrayFetcher.kt index 84bab7f640..6cdc4f5b72 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/ByteArrayFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/ByteArrayFetcher.kt @@ -21,7 +21,6 @@ class ByteArrayFetcher : Fetcher { size: Size, options: Options ): FetchResult { - val source = ByteArrayInputStream(data).source().buffer() return SourceResult( source = ByteArrayInputStream(data).source().buffer(), mimeType = "image/gif", diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/CoilSetup.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/CoilSetup.kt index 6ccb44b1e3..b6bdcb206e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/CoilSetup.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/CoilSetup.kt @@ -9,7 +9,6 @@ import coil.decode.ImageDecoderDecoder import coil.decode.SvgDecoder import coil.util.CoilUtils import com.chuckerteam.chucker.api.ChuckerInterceptor -import eu.kanade.tachiyomi.R import okhttp3.OkHttpClient class CoilSetup(context: Context) { @@ -19,7 +18,6 @@ class CoilSetup(context: Context) { .crossfade(true) .allowRgb565(true) .allowHardware(false) - .error(R.drawable.ic_broken_image_grey_24dp) .componentRegistry { if (Build.VERSION.SDK_INT >= 28) { add(ImageDecoderDecoder()) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/MangaFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/MangaFetcher.kt index ac0f86cda5..7c469d8490 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/MangaFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/coil/MangaFetcher.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.data.download.coil +import android.webkit.MimeTypeMap import coil.bitmappool.BitmapPool import coil.decode.DataSource import coil.decode.Options @@ -10,11 +11,15 @@ import coil.size.Size import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.network.NetworkHelper +import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.storage.DiskUtil +import okhttp3.CacheControl import okhttp3.Call import okhttp3.Request +import okhttp3.Response +import okhttp3.ResponseBody import okio.buffer import okio.sink import okio.source @@ -29,55 +34,95 @@ class MangaFetcher() : Fetcher { private val sourceManager: SourceManager by injectLazy() private val defaultClient = Injekt.get().client - override fun key(manga: Manga): String? { - if (manga.thumbnail_url.isNullOrBlank()) return null - return DiskUtil.hashKeyForDisk(manga.thumbnail_url!!) + override fun key(data: Manga): String? { + if (data.thumbnail_url.isNullOrBlank()) return null + return if (!data.favorite) { + data.thumbnail_url!! + } else { + DiskUtil.hashKeyForDisk(data.thumbnail_url!!) + } } - override suspend fun fetch(pool: BitmapPool, manga: Manga, size: Size, options: Options): FetchResult { - val cover = manga.thumbnail_url - when (getResourceType(cover)) { - Type.File -> { - return fileLoader(manga) - } - Type.URL -> { - return httpLoader(manga) - } - Type.CUSTOM -> { - return customLoader(manga) - } + override suspend fun fetch(pool: BitmapPool, data: Manga, size: Size, options: Options): FetchResult { + val cover = data.thumbnail_url + return when (getResourceType(cover)) { + Type.File -> fileLoader(data) + Type.URL -> httpLoader(data, options) + Type.CUSTOM -> customLoader(data, options) null -> error("Invalid image") } } - private fun customLoader(manga: Manga): FetchResult { + private suspend fun customLoader(manga: Manga, options: Options): FetchResult { val coverFile = coverCache.getCoverFile(manga) if (coverFile.exists()) { return fileLoader(coverFile) } manga.thumbnail_url = manga.thumbnail_url!!.substringAfter("-J2K-").substringAfter("CUSTOM-") - return httpLoader(manga) + return httpLoader(manga, options) } - private fun httpLoader(manga: Manga): FetchResult { + private suspend fun httpLoader(manga: Manga, options: Options): FetchResult { val coverFile = coverCache.getCoverFile(manga) if (coverFile.exists()) { return fileLoader(coverFile) } - val call = getCall(manga) - val tmpFile = File(coverFile.absolutePath + "_tmp") + if (!manga.favorite) { + val (response, body) = awaitGetCall(manga) - val response = call.execute() - val body = checkNotNull(response.body) { "Null response source" } + return SourceResult( + source = body.source(), + mimeType = getMimeType(manga.thumbnail_url!!, body), + dataSource = if (response.cacheResponse != null) DataSource.DISK else DataSource.NETWORK + ) + } else { + val (_, body) = awaitGetCall(manga, !options.networkCachePolicy.readEnabled) - body.source().use { input -> - tmpFile.sink().buffer().use { output -> - output.writeAll(input) + val tmpFile = File(coverFile.absolutePath + "_tmp") + body.source().use { input -> + tmpFile.sink().buffer().use { output -> + output.writeAll(input) + } } + + tmpFile.renameTo(coverFile) + return fileLoader(coverFile) + } + } + + private suspend fun awaitGetCall(manga: Manga, onlyCache: Boolean = false): Pair { + val call = getCall(manga, onlyCache) + val response = call.await() + return response to checkNotNull(response.body) { "Null response source" } + } + + /** + * "text/plain" is often used as a default/fallback MIME type. + * Attempt to guess a better MIME type from the file extension. + */ + private fun getMimeType(data: String, body: ResponseBody): String? { + val rawContentType = body.contentType()?.toString() + return if (rawContentType == null || rawContentType.startsWith("text/plain")) { + MimeTypeMap.getSingleton().getMimeTypeFromUrl(data) ?: rawContentType + } else { + rawContentType + } + } + + /** Modified from [MimeTypeMap.getFileExtensionFromUrl] to be more permissive with special characters. */ + private fun MimeTypeMap.getMimeTypeFromUrl(url: String?): String? { + if (url.isNullOrBlank()) { + return null } - tmpFile.renameTo(coverFile) - return fileLoader(coverFile) + val extension = url + .substringBeforeLast('#') // Strip the fragment. + .substringBeforeLast('?') // Strip the query. + .substringAfterLast('/') // Get the last path segment. + .substringAfterLast('.', missingDelimiterValue = "") // Get the file extension. + + return getMimeTypeFromExtension(extension) } private fun fileLoader(manga: Manga): FetchResult { @@ -92,18 +137,19 @@ class MangaFetcher() : Fetcher { ) } - private fun getCall(manga: Manga): Call { + private fun getCall(manga: Manga, onlyCache: Boolean): Call { val source = sourceManager.get(manga.source) as? HttpSource val client = source?.client ?: defaultClient - val newClient = client.newBuilder() - .cache(coverCache.cache) - .build() + val newClient = client.newBuilder().build() val request = Request.Builder().url(manga.thumbnail_url!!).also { if (source != null) { it.headers(source.headers) } + if (onlyCache) { + it.cacheControl(CacheControl.FORCE_CACHE) + } }.build() return newClient.newCall(request) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index bbd883dad3..8bcc845c63 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -16,6 +16,7 @@ import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat import coil.Coil import coil.request.CachePolicy +import coil.request.GetRequest import coil.request.LoadRequest import coil.transform.CircleCropTransformation import eu.kanade.tachiyomi.R @@ -603,7 +604,7 @@ class LibraryUpdateService( * * @param updates a list of manga with new updates. */ - private fun showResultNotification(updates: Map>) { + private suspend fun showResultNotification(updates: Map>) { val notifications = ArrayList>() updates.forEach { val manga = it.key @@ -613,11 +614,15 @@ class LibraryUpdateService( setSmallIcon(R.drawable.ic_tachi) try { - val request = LoadRequest.Builder(this@LibraryUpdateService).data(manga) - .transformations(CircleCropTransformation()).size(width = 256, height = 256) - .target { drawable -> setLargeIcon((drawable as BitmapDrawable).bitmap) }.build() + val request = GetRequest.Builder(this@LibraryUpdateService).data(manga) + .networkCachePolicy(CachePolicy.DISABLED) + .transformations(CircleCropTransformation()).size(width = 256, height = 256) + .build() - Coil.imageLoader(this@LibraryUpdateService).execute(request) + Coil.imageLoader(this@LibraryUpdateService) + .execute(request).drawable?.let { drawable -> + setLargeIcon((drawable as BitmapDrawable).bitmap) + } } catch (e: Exception) { } setGroupAlertBehavior(GROUP_ALERT_SUMMARY) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt index 748e79144c..44eb38672c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt @@ -4,7 +4,6 @@ import android.view.View import android.view.ViewGroup import coil.api.clear import coil.api.loadAny -import coil.transform.RoundedCornersTransformation import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.view.gone @@ -12,9 +11,7 @@ import eu.kanade.tachiyomi.util.view.updateLayoutParams import eu.kanade.tachiyomi.util.view.visible import eu.kanade.tachiyomi.util.view.visibleIf import kotlinx.android.synthetic.main.manga_list_item.* -import kotlinx.android.synthetic.main.manga_list_item.title import kotlinx.android.synthetic.main.manga_list_item.view.* -import kotlinx.android.synthetic.main.recently_read_item.* import kotlinx.android.synthetic.main.unread_download_badge.* /** @@ -82,9 +79,7 @@ class LibraryListHolder( cover_thumbnail.clear() } else { val id = item.manga.id ?: return - cover_thumbnail.loadAny(item.manga) { - transformations(RoundedCornersTransformation(2f, 2f, 2f, 2f)) - } + cover_thumbnail.loadAny(item.manga) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index af7c81cccf..f21c2f448b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -39,8 +39,6 @@ import androidx.transition.ChangeImageTransform import androidx.transition.TransitionManager import androidx.transition.TransitionSet import coil.Coil -import coil.api.clear -import coil.api.loadAny import coil.request.LoadRequest import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.checkbox.checkBoxPrompt @@ -308,7 +306,6 @@ class MangaDetailsController : BaseController, /** Get the color of the manga cover*/ fun setPaletteColor() { val view = view ?: return - coverColor = null val request = LoadRequest.Builder(view.context).data(manga).allowHardware(false) .target { drawable -> @@ -330,16 +327,12 @@ class MangaDetailsController : BaseController, activity?.window?.statusBarColor = translucentColor } } + manga_cover_full.setImageDrawable(drawable) + getHeader()?.updateCover(manga!!) }.build() Coil.imageLoader(view.context).execute(request) } - fun resetCovers() { - manga_cover_full.clear() - manga_cover_full.loadAny(manga) - getHeader()?.updateCover(manga!!, true) - } - /** Set toolbar theme for themes that are inverted (ie. light blue theme) */ private fun setActionBar(forThis: Boolean) { val activity = activity ?: return @@ -1258,7 +1251,6 @@ class MangaDetailsController : BaseController, currentAnimator?.cancel() // Load the high-resolution "zoomed-in" image. - manga_cover_full?.loadAny(manga) val expandedImageView = manga_cover_full ?: return val fullBackdrop = full_backdrop diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt index d4204cfd7a..fa6f3adae4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt @@ -748,7 +748,6 @@ class MangaDetailsPresenter( fun forceUpdateCovers(deleteCache: Boolean = true) { if (deleteCache) coverCache.deleteFromCache(manga) controller.setPaletteColor() - controller.resetCovers() } fun editCoverWithStream(uri: Uri): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt index cec007dbc0..6b49dac3fe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt @@ -9,8 +9,8 @@ import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils -import coil.api.clear import coil.api.loadAny +import coil.request.CachePolicy import com.google.android.material.button.MaterialButton import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga @@ -308,12 +308,19 @@ class MangaHeaderHolder( } } - fun updateCover(manga: Manga, forceUpdate: Boolean = false) { - if (!isCached(manga) && !forceUpdate) return - manga_cover.clear() - backdrop.clear() - manga_cover.loadAny(manga) - backdrop.loadAny(manga) + fun updateCover(manga: Manga) { + if (!manga.initialized) return + val drawable = adapter.controller.manga_cover_full?.drawable + manga_cover.loadAny(manga, builder = { + placeholder(drawable) + error(drawable) + if (manga.favorite) networkCachePolicy(CachePolicy.DISABLED) + }) + backdrop.loadAny(manga, builder = { + placeholder(drawable) + error(drawable) + if (manga.favorite) networkCachePolicy(CachePolicy.DISABLED) + }) } private fun isCached(manga: Manga): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceGridHolder.kt index f13dedb2fc..b8d5da170b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceGridHolder.kt @@ -13,8 +13,6 @@ import eu.kanade.tachiyomi.ui.library.LibraryCategoryAdapter import eu.kanade.tachiyomi.util.view.gone import eu.kanade.tachiyomi.widget.CoverViewTarget import kotlinx.android.synthetic.main.manga_grid_item.* -import kotlinx.android.synthetic.main.manga_grid_item.cover_thumbnail -import kotlinx.android.synthetic.main.manga_grid_item.title import kotlinx.android.synthetic.main.unread_download_badge.* /** @@ -63,7 +61,8 @@ class BrowseSourceGridHolder( cover_thumbnail.clear() } else { val id = manga.id ?: return - val request = LoadRequest.Builder(view.context).data(manga).target(CoverViewTarget(cover_thumbnail, progress)).build() + val request = LoadRequest.Builder(view.context).data(manga) + .target(CoverViewTarget(cover_thumbnail, progress)).build() Coil.imageLoader(view.context).execute(request) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceListHolder.kt index 0b0ad79027..9d309afafe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceListHolder.kt @@ -5,7 +5,6 @@ import androidx.recyclerview.widget.RecyclerView import coil.Coil import coil.api.clear import coil.request.LoadRequest -import coil.transform.RoundedCornersTransformation import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.tachiyomi.R @@ -48,8 +47,8 @@ class BrowseSourceListHolder(private val view: View, adapter: FlexibleAdapter