From 9acf4e3800414c65899a3e0d9402f715683b98e3 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Sun, 10 Nov 2024 15:20:26 +0100 Subject: [PATCH] Upgrade to Coil 3 --- .../data/service/BackgroundService.kt | 8 ++-- .../org/jellyfin/androidtv/di/AppModule.kt | 14 ++++--- .../integration/dream/DreamViewModel.kt | 10 ++--- .../integration/provider/ImageProvider.kt | 9 +++-- .../jellyfin/androidtv/ui/AsyncImageView.kt | 14 ++++--- .../ui/playback/overlay/CustomSeekProvider.kt | 37 +++++++++++-------- .../overlay/LeanbackOverlayFragment.java | 2 +- .../screen/DeveloperPreferencesScreen.kt | 4 +- .../util/coil/SubsetTransformation.kt | 6 +-- gradle/libs.versions.toml | 12 +++--- 10 files changed, 66 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/data/service/BackgroundService.kt b/app/src/main/java/org/jellyfin/androidtv/data/service/BackgroundService.kt index 9ed5f36df2..b87687ea71 100644 --- a/app/src/main/java/org/jellyfin/androidtv/data/service/BackgroundService.kt +++ b/app/src/main/java/org/jellyfin/androidtv/data/service/BackgroundService.kt @@ -3,9 +3,9 @@ package org.jellyfin.androidtv.data.service import android.content.Context import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asImageBitmap -import androidx.core.graphics.drawable.toBitmap -import coil.ImageLoader -import coil.request.ImageRequest +import coil3.ImageLoader +import coil3.request.ImageRequest +import coil3.toBitmap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.MainScope @@ -124,7 +124,7 @@ class BackgroundService( _backgrounds = backdropUrls.mapNotNull { url -> imageLoader.execute( request = ImageRequest.Builder(context).data(url).build() - ).drawable?.toBitmap()?.asImageBitmap() + ).image?.toBitmap()?.asImageBitmap() } // Go to first background diff --git a/app/src/main/java/org/jellyfin/androidtv/di/AppModule.kt b/app/src/main/java/org/jellyfin/androidtv/di/AppModule.kt index c0be50279a..4405f97e8d 100644 --- a/app/src/main/java/org/jellyfin/androidtv/di/AppModule.kt +++ b/app/src/main/java/org/jellyfin/androidtv/di/AppModule.kt @@ -2,10 +2,12 @@ package org.jellyfin.androidtv.di import android.content.Context import android.os.Build -import coil.ImageLoader -import coil.decode.GifDecoder -import coil.decode.ImageDecoderDecoder -import coil.decode.SvgDecoder +import coil3.ImageLoader +import coil3.gif.AnimatedImageDecoder +import coil3.gif.GifDecoder +import coil3.network.okhttp.OkHttpNetworkFetcherFactory +import coil3.serviceLoaderEnabled +import coil3.svg.SvgDecoder import org.jellyfin.androidtv.BuildConfig import org.jellyfin.androidtv.auth.repository.ServerRepository import org.jellyfin.androidtv.auth.repository.UserRepository @@ -103,8 +105,10 @@ val appModule = module { // Coil (images) single { ImageLoader.Builder(androidContext()).apply { + serviceLoaderEnabled(false) components { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) add(ImageDecoderDecoder.Factory()) + add(OkHttpNetworkFetcherFactory()) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) add(AnimatedImageDecoder.Factory()) else add(GifDecoder.Factory()) add(SvgDecoder.Factory()) } diff --git a/app/src/main/java/org/jellyfin/androidtv/integration/dream/DreamViewModel.kt b/app/src/main/java/org/jellyfin/androidtv/integration/dream/DreamViewModel.kt index c7b9f3aea3..ffd5b1cdbc 100644 --- a/app/src/main/java/org/jellyfin/androidtv/integration/dream/DreamViewModel.kt +++ b/app/src/main/java/org/jellyfin/androidtv/integration/dream/DreamViewModel.kt @@ -2,11 +2,11 @@ package org.jellyfin.androidtv.integration.dream import android.annotation.SuppressLint import android.content.Context -import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import coil.ImageLoader -import coil.request.ImageRequest +import coil3.ImageLoader +import coil3.request.ImageRequest +import coil3.toBitmap import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async @@ -123,13 +123,13 @@ class DreamViewModel( val logoDeferred = async { imageLoader.execute( request = ImageRequest.Builder(context).data(logoUrl).build() - ).drawable?.toBitmap() + ).image?.toBitmap() } val backdropDeferred = async { imageLoader.execute( request = ImageRequest.Builder(context).data(backdropUrl).build() - ).drawable?.toBitmap() + ).image?.toBitmap() } awaitAll(logoDeferred, backdropDeferred) diff --git a/app/src/main/java/org/jellyfin/androidtv/integration/provider/ImageProvider.kt b/app/src/main/java/org/jellyfin/androidtv/integration/provider/ImageProvider.kt index 71a6f82f17..e4f31fdce1 100644 --- a/app/src/main/java/org/jellyfin/androidtv/integration/provider/ImageProvider.kt +++ b/app/src/main/java/org/jellyfin/androidtv/integration/provider/ImageProvider.kt @@ -9,8 +9,9 @@ import android.os.Build import android.os.ParcelFileDescriptor import androidx.core.graphics.drawable.toBitmap import androidx.core.net.toUri -import coil.ImageLoader -import coil.request.ImageRequest +import coil3.ImageLoader +import coil3.asDrawable +import coil3.request.ImageRequest import org.jellyfin.androidtv.BuildConfig import org.jellyfin.androidtv.R import org.koin.android.ext.android.inject @@ -37,8 +38,8 @@ class ImageProvider : ContentProvider() { data(src) error(R.drawable.placeholder_icon) target( - onSuccess = { drawable -> writeDrawable(drawable, outputStream) }, - onError = { drawable -> writeDrawable(requireNotNull(drawable), outputStream) } + onSuccess = { image -> writeDrawable(image.asDrawable(context!!.resources), outputStream) }, + onError = { image -> writeDrawable(requireNotNull(image?.asDrawable(context!!.resources)), outputStream) } ) }.build()) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/AsyncImageView.kt b/app/src/main/java/org/jellyfin/androidtv/ui/AsyncImageView.kt index 326c365a36..35838f99aa 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/AsyncImageView.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/AsyncImageView.kt @@ -9,9 +9,13 @@ import androidx.core.graphics.drawable.toDrawable import androidx.core.view.doOnAttach import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope -import coil.ImageLoader -import coil.request.ImageRequest -import coil.transform.CircleCropTransformation +import coil3.ImageLoader +import coil3.asImage +import coil3.request.ImageRequest +import coil3.request.crossfade +import coil3.request.target +import coil3.request.transformations +import coil3.transform.CircleCropTransformation import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -88,9 +92,9 @@ class AsyncImageView @JvmOverloads constructor( target(this@AsyncImageView) data(url) - placeholder(placeholderOrBlurHash) + placeholder(placeholderOrBlurHash?.asImage()) if (circleCrop) transformations(CircleCropTransformation()) - error(placeholder) + error(placeholder?.asImage()) }.build()) } } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/CustomSeekProvider.kt b/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/CustomSeekProvider.kt index 725f9a473f..f256074432 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/CustomSeekProvider.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/CustomSeekProvider.kt @@ -1,12 +1,15 @@ package org.jellyfin.androidtv.ui.playback.overlay import android.content.Context -import androidx.core.graphics.drawable.toBitmap import androidx.leanback.widget.PlaybackSeekDataProvider -import coil.ImageLoader -import coil.request.Disposable -import coil.request.ImageRequest -import coil.size.Size +import coil3.ImageLoader +import coil3.network.NetworkHeaders +import coil3.network.httpHeaders +import coil3.request.Disposable +import coil3.request.ImageRequest +import coil3.request.transformations +import coil3.size.Size +import coil3.toBitmap import org.jellyfin.androidtv.util.coil.SubsetTransformation import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.extensions.trickplayApi @@ -70,22 +73,24 @@ class CustomSeekProvider( imageRequests[index] = imageLoader.enqueue(ImageRequest.Builder(context).apply { data(url) size(Size.ORIGINAL) - addHeader( - "Authorization", - AuthorizationHeaderBuilder.buildHeader( - api.clientInfo.name, - api.clientInfo.version, - api.deviceInfo.id, - api.deviceInfo.name, - api.accessToken + httpHeaders(NetworkHeaders.Builder().apply { + set( + key = "Authorization", + value = AuthorizationHeaderBuilder.buildHeader( + api.clientInfo.name, + api.clientInfo.version, + api.deviceInfo.id, + api.deviceInfo.name, + api.accessToken + ) ) - ) + }.build()) transformations(SubsetTransformation(offsetX, offsetY, trickPlayInfo.width, trickPlayInfo.height)) target( - onSuccess = { result -> - val bitmap = result.current.toBitmap() + onSuccess = { image -> + val bitmap = image.toBitmap() callback.onThumbnailLoaded(bitmap, index) } ) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/LeanbackOverlayFragment.java b/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/LeanbackOverlayFragment.java index 02f3133155..ea18d7bafc 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/LeanbackOverlayFragment.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/LeanbackOverlayFragment.java @@ -16,7 +16,7 @@ import org.jellyfin.androidtv.ui.playback.PlaybackControllerContainer; import org.jellyfin.sdk.api.client.ApiClient; -import coil.ImageLoader; +import coil3.ImageLoader; import kotlin.Lazy; import timber.log.Timber; diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/preference/screen/DeveloperPreferencesScreen.kt b/app/src/main/java/org/jellyfin/androidtv/ui/preference/screen/DeveloperPreferencesScreen.kt index 217458b576..e1f42e9c45 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/preference/screen/DeveloperPreferencesScreen.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/preference/screen/DeveloperPreferencesScreen.kt @@ -1,8 +1,8 @@ package org.jellyfin.androidtv.ui.preference.screen import android.text.format.Formatter -import coil.ImageLoader -import coil.annotation.ExperimentalCoilApi +import coil3.ImageLoader +import coil3.annotation.ExperimentalCoilApi import org.jellyfin.androidtv.BuildConfig import org.jellyfin.androidtv.R import org.jellyfin.androidtv.preference.SystemPreferences diff --git a/app/src/main/java/org/jellyfin/androidtv/util/coil/SubsetTransformation.kt b/app/src/main/java/org/jellyfin/androidtv/util/coil/SubsetTransformation.kt index c49b775b53..2431394bc1 100644 --- a/app/src/main/java/org/jellyfin/androidtv/util/coil/SubsetTransformation.kt +++ b/app/src/main/java/org/jellyfin/androidtv/util/coil/SubsetTransformation.kt @@ -1,15 +1,15 @@ package org.jellyfin.androidtv.util.coil import android.graphics.Bitmap -import coil.size.Size -import coil.transform.Transformation +import coil3.size.Size +import coil3.transform.Transformation class SubsetTransformation( private val x: Int, private val y: Int, private val width: Int, private val height: Int, -) : Transformation { +) : Transformation() { override val cacheKey: String = "$x,$y,$width,$height" override suspend fun transform( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f11b812f76..9668bb328a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,7 +24,7 @@ androidx-tv = "1.0.0" androidx-tvprovider = "1.1.0-alpha01" androidx-window = "1.3.0" androidx-work = "2.9.1" -coil = "2.7.0" +coil = "3.0.2" detekt = "1.23.7" java-jdk = "21" jellyfin-androidx-media = "1.3.1+2" @@ -101,9 +101,10 @@ markwon-core = { module = "io.noties.markwon:core", version.ref = "markwon" } markwon-html = { module = "io.noties.markwon:html", version.ref = "markwon" } # Image utility -coil-base = { module = "io.coil-kt:coil-base", version.ref = "coil" } -coil-gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" } -coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" } +coil-core = { module = "io.coil-kt.coil3:coil-core", version.ref = "coil" } +coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" } +coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil" } +coil-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil" } # Crash Reporting acra-core = { module = "ch.acra:acra-core", version.ref = "acra" } @@ -141,7 +142,8 @@ androidx-lifecycle = [ "androidx-lifecycle-viewmodel", ] coil = [ - "coil-base", + "coil-core", + "coil-network-okhttp", "coil-gif", "coil-svg", ]