refactor: switch to coil for image loading (#388)

This commit is contained in:
Jarne Demeulemeester 2023-05-29 16:44:03 +02:00 committed by GitHub
parent 3fc72bf8b3
commit 1399c77b5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 44 additions and 75 deletions

View file

@ -108,7 +108,8 @@ dependencies {
implementation(libs.androidx.room.ktx) implementation(libs.androidx.room.ktx)
implementation(libs.androidx.swiperefreshlayout) implementation(libs.androidx.swiperefreshlayout)
implementation(libs.androidx.work) implementation(libs.androidx.work)
implementation(libs.glide) implementation(libs.coil)
implementation(libs.coil.svg)
implementation(libs.hilt.android) implementation(libs.hilt.android)
kapt(libs.hilt.compiler) kapt(libs.hilt.compiler)
implementation(libs.jellyfin.core) implementation(libs.jellyfin.core)

View file

@ -4,13 +4,18 @@ import android.app.Application
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.hilt.work.HiltWorkerFactory import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration 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 com.google.android.material.color.DynamicColors
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject import javax.inject.Inject
import timber.log.Timber import timber.log.Timber
@HiltAndroidApp @HiltAndroidApp
class BaseApplication : Application(), Configuration.Provider { class BaseApplication : Application(), Configuration.Provider, ImageLoaderFactory {
@Inject @Inject
lateinit var appPreferences: AppPreferences lateinit var appPreferences: AppPreferences
@ -37,4 +42,19 @@ class BaseApplication : Application(), Configuration.Provider {
if (appPreferences.dynamicColors) DynamicColors.applyToActivitiesIfAvailable(this) 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()
}
} }

View file

@ -5,8 +5,7 @@ import android.widget.ImageView
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.databinding.BindingAdapter import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import coil.load
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
import dev.jdtech.jellyfin.adapters.ServerGridAdapter import dev.jdtech.jellyfin.adapters.ServerGridAdapter
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
@ -49,7 +48,6 @@ fun bindItemImage(imageView: ImageView, item: BaseItemDto) {
fun bindItemImage(imageView: ImageView, item: FindroidItem) { fun bindItemImage(imageView: ImageView, item: FindroidItem) {
val itemId = when (item) { val itemId = when (item) {
is FindroidEpisode -> item.seriesId is FindroidEpisode -> item.seriesId
// is JellyfinSeasonItem && item.imageTags.isNullOrEmpty() -> item.seriesId
else -> item.id else -> item.id
} }
@ -111,18 +109,15 @@ fun bindUserImage(imageView: ImageView, user: User) {
private fun ImageView.loadImage( private fun ImageView.loadImage(
url: String, url: String,
@DrawableRes placeholderId: Int = CoreR.color.neutral_800, @DrawableRes placeholderId: Int = CoreR.color.neutral_800
@DrawableRes errorPlaceHolderId: Int? = null
): View { ): View {
val api = JellyfinApi.getInstance(context.applicationContext) val api = JellyfinApi.getInstance(context.applicationContext)
Glide this.load("${api.api.baseUrl}$url") {
.with(context) crossfade(true)
.load("${api.api.baseUrl}$url") placeholder(placeholderId)
.transition(DrawableTransitionOptions.withCrossFade()) error(placeholderId)
.placeholder(placeholderId) }
.error(errorPlaceHolderId)
.into(this)
return this return this
} }

View file

@ -1,12 +1,13 @@
package dev.jdtech.jellyfin.utils package dev.jdtech.jellyfin.utils
import android.graphics.Bitmap
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.ui.TimeBar import androidx.media3.ui.TimeBar
import com.bumptech.glide.Glide import coil.load
import com.bumptech.glide.load.resource.bitmap.RoundedCorners import coil.transform.RoundedCornersTransformation
import dev.jdtech.jellyfin.utils.bif.BifData import dev.jdtech.jellyfin.utils.bif.BifData
import dev.jdtech.jellyfin.utils.bif.BifUtil import dev.jdtech.jellyfin.utils.bif.BifUtil
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -19,7 +20,8 @@ class PreviewScrubListener(
private val currentTrickPlay: StateFlow<BifData?> private val currentTrickPlay: StateFlow<BifData?>
) : TimeBar.OnScrubListener { ) : TimeBar.OnScrubListener {
private val roundedCorners = RoundedCorners(10) private val roundedCorners = RoundedCornersTransformation(10f)
private var currentBitMap: Bitmap? = null
override fun onScrubStart(timeBar: TimeBar, position: Long) { override fun onScrubStart(timeBar: TimeBar, position: Long) {
Timber.d("Scrubbing started at $position") Timber.d("Scrubbing started at $position")
@ -54,10 +56,12 @@ class PreviewScrubListener(
scrubbingPreview.x = layoutX scrubbingPreview.x = layoutX
Glide.with(scrubbingPreview) if (currentBitMap != image) {
.load(image) scrubbingPreview.load(image) {
.transform(roundedCorners) transformations(roundedCorners)
.into(scrubbingPreview) }
currentBitMap = image
}
} }
override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) { override fun onScrubStop(timeBar: TimeBar, position: Long, canceled: Boolean) {

View file

@ -62,8 +62,6 @@ dependencies {
kapt(libs.androidx.room.compiler) kapt(libs.androidx.room.compiler)
implementation(libs.androidx.room.ktx) implementation(libs.androidx.room.ktx)
implementation(libs.androidx.work) implementation(libs.androidx.work)
implementation(libs.glide)
kapt(libs.glide.compiler)
implementation(libs.hilt.android) implementation(libs.hilt.android)
kapt(libs.hilt.compiler) kapt(libs.hilt.compiler)
implementation(libs.jellyfin.core) implementation(libs.jellyfin.core)

View file

@ -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
)
)
}
}

View file

@ -17,7 +17,7 @@ androidx-recyclerview-selection = "1.1.0"
androidx-room = "2.5.1" androidx-room = "2.5.1"
androidx-swiperefreshlayout = "1.1.0" androidx-swiperefreshlayout = "1.1.0"
androidx-work = "2.8.1" androidx-work = "2.8.1"
glide = "4.15.1" coil = "2.4.0"
hilt = "2.46.1" hilt = "2.46.1"
jellyfin = "1.4.2" jellyfin = "1.4.2"
kotlin = "1.8.21" 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-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidx-room" }
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidx-swiperefreshlayout" } androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidx-swiperefreshlayout" }
androidx-work = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" } androidx-work = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" }
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } coil = { module = "io.coil-kt:coil", version.ref = "coil" }
glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" } coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
hilt-compiler = { module = "com.google.dagger:hilt-compiler", 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" } jellyfin-core = { module = "org.jellyfin.sdk:jellyfin-core", version.ref = "jellyfin" }