From 1399c77b5dfb65051a0ad57f699722b8ff8337e9 Mon Sep 17 00:00:00 2001 From: Jarne Demeulemeester Date: Mon, 29 May 2023 16:44:03 +0200 Subject: [PATCH] refactor: switch to coil for image loading (#388) --- app/phone/build.gradle.kts | 3 +- .../dev/jdtech/jellyfin/BaseApplication.kt | 22 ++++++++- .../dev/jdtech/jellyfin/BindingAdapters.kt | 19 +++---- .../jellyfin/utils/PreviewScrubListener.kt | 18 ++++--- core/build.gradle.kts | 2 - .../dev/jdtech/jellyfin/di/GlideModule.kt | 49 ------------------- gradle/libs.versions.toml | 6 +-- 7 files changed, 44 insertions(+), 75 deletions(-) delete mode 100644 core/src/main/java/dev/jdtech/jellyfin/di/GlideModule.kt diff --git a/app/phone/build.gradle.kts b/app/phone/build.gradle.kts index 4684e17b..a9ae89b3 100644 --- a/app/phone/build.gradle.kts +++ b/app/phone/build.gradle.kts @@ -108,7 +108,8 @@ dependencies { implementation(libs.androidx.room.ktx) implementation(libs.androidx.swiperefreshlayout) implementation(libs.androidx.work) - implementation(libs.glide) + implementation(libs.coil) + implementation(libs.coil.svg) implementation(libs.hilt.android) kapt(libs.hilt.compiler) implementation(libs.jellyfin.core) diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/BaseApplication.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/BaseApplication.kt index de37337a..ffed0d1e 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/BaseApplication.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/BaseApplication.kt @@ -4,13 +4,18 @@ import android.app.Application import androidx.appcompat.app.AppCompatDelegate import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration +import coil.ImageLoader +import coil.ImageLoaderFactory +import coil.decode.SvgDecoder +import coil.disk.DiskCache +import coil.request.CachePolicy import com.google.android.material.color.DynamicColors import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject import timber.log.Timber @HiltAndroidApp -class BaseApplication : Application(), Configuration.Provider { +class BaseApplication : Application(), Configuration.Provider, ImageLoaderFactory { @Inject lateinit var appPreferences: AppPreferences @@ -37,4 +42,19 @@ class BaseApplication : Application(), Configuration.Provider { if (appPreferences.dynamicColors) DynamicColors.applyToActivitiesIfAvailable(this) } + + override fun newImageLoader(): ImageLoader { + return ImageLoader.Builder(this) + .components { + add(SvgDecoder.Factory()) + } + .diskCachePolicy(if (appPreferences.imageCache) CachePolicy.ENABLED else CachePolicy.DISABLED) + .diskCache { + DiskCache.Builder() + .directory(this.cacheDir.resolve("image_cache")) + .maxSizeBytes(appPreferences.imageCacheSize * 1024L * 1024) + .build() + } + .build() + } } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt index 2ba62bcc..d386e894 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt @@ -5,8 +5,7 @@ import android.widget.ImageView import androidx.annotation.DrawableRes import androidx.databinding.BindingAdapter import androidx.recyclerview.widget.RecyclerView -import com.bumptech.glide.Glide -import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import coil.load import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter import dev.jdtech.jellyfin.adapters.ServerGridAdapter import dev.jdtech.jellyfin.adapters.ViewItemListAdapter @@ -49,7 +48,6 @@ fun bindItemImage(imageView: ImageView, item: BaseItemDto) { fun bindItemImage(imageView: ImageView, item: FindroidItem) { val itemId = when (item) { is FindroidEpisode -> item.seriesId -// is JellyfinSeasonItem && item.imageTags.isNullOrEmpty() -> item.seriesId else -> item.id } @@ -111,18 +109,15 @@ fun bindUserImage(imageView: ImageView, user: User) { private fun ImageView.loadImage( url: String, - @DrawableRes placeholderId: Int = CoreR.color.neutral_800, - @DrawableRes errorPlaceHolderId: Int? = null + @DrawableRes placeholderId: Int = CoreR.color.neutral_800 ): View { val api = JellyfinApi.getInstance(context.applicationContext) - Glide - .with(context) - .load("${api.api.baseUrl}$url") - .transition(DrawableTransitionOptions.withCrossFade()) - .placeholder(placeholderId) - .error(errorPlaceHolderId) - .into(this) + this.load("${api.api.baseUrl}$url") { + crossfade(true) + placeholder(placeholderId) + error(placeholderId) + } return this } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/utils/PreviewScrubListener.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/utils/PreviewScrubListener.kt index 9d8b6617..a5e10d39 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/utils/PreviewScrubListener.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/utils/PreviewScrubListener.kt @@ -1,12 +1,13 @@ package dev.jdtech.jellyfin.utils +import android.graphics.Bitmap import android.view.View import android.view.ViewGroup import android.widget.ImageView import androidx.media3.common.Player import androidx.media3.ui.TimeBar -import com.bumptech.glide.Glide -import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import coil.load +import coil.transform.RoundedCornersTransformation import dev.jdtech.jellyfin.utils.bif.BifData import dev.jdtech.jellyfin.utils.bif.BifUtil import kotlinx.coroutines.flow.StateFlow @@ -19,7 +20,8 @@ class PreviewScrubListener( private val currentTrickPlay: StateFlow ) : TimeBar.OnScrubListener { - private val roundedCorners = RoundedCorners(10) + private val roundedCorners = RoundedCornersTransformation(10f) + private var currentBitMap: Bitmap? = null override fun onScrubStart(timeBar: TimeBar, position: Long) { Timber.d("Scrubbing started at $position") @@ -54,10 +56,12 @@ class PreviewScrubListener( scrubbingPreview.x = layoutX - Glide.with(scrubbingPreview) - .load(image) - .transform(roundedCorners) - .into(scrubbingPreview) + if (currentBitMap != image) { + scrubbingPreview.load(image) { + transformations(roundedCorners) + } + currentBitMap = image + } } override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 8bd3859b..f420ebc4 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -62,8 +62,6 @@ dependencies { kapt(libs.androidx.room.compiler) implementation(libs.androidx.room.ktx) implementation(libs.androidx.work) - implementation(libs.glide) - kapt(libs.glide.compiler) implementation(libs.hilt.android) kapt(libs.hilt.compiler) implementation(libs.jellyfin.core) diff --git a/core/src/main/java/dev/jdtech/jellyfin/di/GlideModule.kt b/core/src/main/java/dev/jdtech/jellyfin/di/GlideModule.kt deleted file mode 100644 index 8f02e0e1..00000000 --- a/core/src/main/java/dev/jdtech/jellyfin/di/GlideModule.kt +++ /dev/null @@ -1,49 +0,0 @@ -package dev.jdtech.jellyfin.di - -import android.content.Context -import androidx.preference.PreferenceManager -import com.bumptech.glide.GlideBuilder -import com.bumptech.glide.annotation.GlideModule -import com.bumptech.glide.load.engine.DiskCacheStrategy.NONE -import com.bumptech.glide.load.engine.DiskCacheStrategy.RESOURCE -import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory -import com.bumptech.glide.module.AppGlideModule -import com.bumptech.glide.request.RequestOptions -import dev.jdtech.jellyfin.AppPreferences -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.launch -import timber.log.Timber - -@GlideModule -class GlideModule : AppGlideModule() { - override fun applyOptions(context: Context, builder: GlideBuilder) { - val preferences = AppPreferences(PreferenceManager.getDefaultSharedPreferences(context)) - val use = preferences.imageCache - - if (use) { - val sizeMb = preferences.imageCacheSize - val sizeB = 1024L * 1024 * sizeMb - Timber.d("Setting image cache to use $sizeMb MB of disk space") - - builder.setDiskCache(InternalCacheDiskCacheFactory(context, sizeB)) - builder.caching(enabled = true) - } else { - builder.caching(enabled = false) - Timber.d("Image cache disabled. Clearing all persisted data.") - - MainScope().launch { - GlideApp.getPhotoCacheDir(context)?.also { - if (it.exists()) it.deleteRecursively() - } - } - } - } - - private fun GlideBuilder.caching(enabled: Boolean) { - setDefaultRequestOptions( - RequestOptions().diskCacheStrategy( - if (enabled) RESOURCE else NONE - ) - ) - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 354aedd1..60121d70 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ androidx-recyclerview-selection = "1.1.0" androidx-room = "2.5.1" androidx-swiperefreshlayout = "1.1.0" androidx-work = "2.8.1" -glide = "4.15.1" +coil = "2.4.0" hilt = "2.46.1" jellyfin = "1.4.2" kotlin = "1.8.21" @@ -55,8 +55,8 @@ androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidx-room" } androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidx-swiperefreshlayout" } androidx-work = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" } -glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } -glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" } +coil = { module = "io.coil-kt:coil", version.ref = "coil" } +coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" } jellyfin-core = { module = "org.jellyfin.sdk:jellyfin-core", version.ref = "jellyfin" }