From f5c91807753ff425b812690820bd20bcf4be1faf Mon Sep 17 00:00:00 2001 From: pluu Date: Fri, 8 Nov 2024 00:10:38 +0900 Subject: [PATCH] landscapist glide to coil --- .../com/pluu/webtoon/utils/ImageLoaderExt.kt | 78 +++++++------------ .../com/pluu/webtoon/utils/ImageViewExt.kt | 11 +-- .../java/com/pluu/webtoon/utils/ToonImage.kt | 67 ++++++++++++++++ .../detail/ui/compose/DetailContentUi.kt | 11 ++- .../episode/ui/compose/EpisodeItemUi.kt | 38 ++------- .../webtoon/weekly/ui/day/WeeklyItemUi.kt | 36 ++------- gradle/libs.versions.toml | 2 +- 7 files changed, 115 insertions(+), 128 deletions(-) create mode 100644 features/ui-common/src/main/java/com/pluu/webtoon/utils/ToonImage.kt diff --git a/features/ui-common/src/main/java/com/pluu/webtoon/utils/ImageLoaderExt.kt b/features/ui-common/src/main/java/com/pluu/webtoon/utils/ImageLoaderExt.kt index 1811bc90..b54f0953 100644 --- a/features/ui-common/src/main/java/com/pluu/webtoon/utils/ImageLoaderExt.kt +++ b/features/ui-common/src/main/java/com/pluu/webtoon/utils/ImageLoaderExt.kt @@ -4,13 +4,15 @@ import android.content.Context import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable -import com.bumptech.glide.Glide -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.target.PreloadTarget -import com.bumptech.glide.request.target.Target +import coil.imageLoader +import coil.request.ImageRequest +import coil.size.Size +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine +import kotlin.coroutines.resume fun Drawable.toLoaderBitmap(): Bitmap? { return when (this) { @@ -23,52 +25,24 @@ suspend fun preLoadImage( context: Context, imageUrl: String ): LoadedState = suspendCancellableCoroutine { cont -> - val requestManager = Glide.with(context) - val target = PreloadTarget.obtain( - requestManager, - PreloadTarget.SIZE_ORIGINAL, - PreloadTarget.SIZE_ORIGINAL - ) - - requestManager - .load(imageUrl.glideUrl()) - .addListener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target, - isFirstResource: Boolean - ): Boolean { - if (cont.isCompleted) { - return false - } - cont.resume( - LoadedState.Error(e ?: IllegalStateException("Unknown Exception")) - ) { _, _, _ -> - requestManager.clear(target) - } - return false - } - - override fun onResourceReady( - resource: Drawable, - model: Any, - target: Target?, - dataSource: DataSource, - isFirstResource: Boolean - ): Boolean { - if (cont.isCompleted) { - return false - } - cont.resume( - LoadedState.Success(resource) - ) { _, _, _ -> - requestManager.clear(target) - } - return false - } - }) - .into(target) + CoroutineScope(Dispatchers.IO).launch( + CoroutineExceptionHandler { _, t -> + cont.resume(LoadedState.Error(t)) + } + ) { + val request = ImageRequest.Builder(context) + .data(imageUrl) + .headers(userAgentHeader) + .size(Size.ORIGINAL) + .allowHardware(false) // Disable hardware bitmaps. + .build() + val drawable = context.imageLoader.execute(request).drawable + if (drawable != null) { + cont.resume(LoadedState.Success(drawable)) + } else { + cont.resume(LoadedState.Error(IllegalStateException("Unknown Exception"))) + } + } } sealed class LoadedState { diff --git a/features/ui-common/src/main/java/com/pluu/webtoon/utils/ImageViewExt.kt b/features/ui-common/src/main/java/com/pluu/webtoon/utils/ImageViewExt.kt index 57009690..5f62a391 100644 --- a/features/ui-common/src/main/java/com/pluu/webtoon/utils/ImageViewExt.kt +++ b/features/ui-common/src/main/java/com/pluu/webtoon/utils/ImageViewExt.kt @@ -1,13 +1,10 @@ package com.pluu.webtoon.utils -import com.bumptech.glide.load.model.GlideUrl -import com.bumptech.glide.load.model.LazyHeaders +import okhttp3.Headers const val userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1" -fun String.glideUrl(): GlideUrl = GlideUrl( - this, LazyHeaders.Builder() - .addHeader("User-Agent", userAgent) - .build() -) +val userAgentHeader = Headers.Builder() + .set("User-Agent", userAgent) + .build() \ No newline at end of file diff --git a/features/ui-common/src/main/java/com/pluu/webtoon/utils/ToonImage.kt b/features/ui-common/src/main/java/com/pluu/webtoon/utils/ToonImage.kt new file mode 100644 index 00000000..2e9e601c --- /dev/null +++ b/features/ui-common/src/main/java/com/pluu/webtoon/utils/ToonImage.kt @@ -0,0 +1,67 @@ +package com.pluu.webtoon.utils + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import coil.request.ImageRequest +import com.pluu.webtoon.ui.compose.theme.themeRed +import com.pluu.webtoon.ui_common.R +import com.skydoves.landscapist.ImageOptions +import com.skydoves.landscapist.coil.CoilImage +import com.skydoves.landscapist.coil.CoilImageState + +@Composable +fun ToonImage( + imageUrl: () -> String, + modifier: Modifier = Modifier, + imageOptions: ImageOptions = ImageOptions( + contentScale = ContentScale.Crop, + alignment = Alignment.Center + ), + onImageStateChanged: (CoilImageState) -> Unit = {}, + previewPlaceholder: Painter? = null, + loading: @Composable (BoxScope.(imageState: CoilImageState.Loading) -> Unit)? = { + CircularProgressIndicator( + modifier = Modifier + .matchParentSize() + .wrapContentSize(), + color = themeRed + ) + }, + success: @Composable (BoxScope.(imageState: CoilImageState.Success, painter: Painter) -> Unit)? = null, + failure: @Composable (BoxScope.(imageState: CoilImageState.Failure) -> Unit)? = { + Image( + painter = painterResource(R.drawable.ic_sentiment_very_dissatisfied_48), + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface), + contentDescription = null + ) + } +) { + val context = LocalContext.current + CoilImage( + imageRequest = { + ImageRequest.Builder(context) + .data(imageUrl.invoke()) + .headers(userAgentHeader) + .crossfade(true) + .build() + }, + modifier = modifier, + imageOptions = imageOptions, + onImageStateChanged = onImageStateChanged, + previewPlaceholder = previewPlaceholder, + loading = loading, + success = success, + failure = failure + ) +} \ No newline at end of file diff --git a/features/ui-detail/src/main/java/com/pluu/webtoon/detail/ui/compose/DetailContentUi.kt b/features/ui-detail/src/main/java/com/pluu/webtoon/detail/ui/compose/DetailContentUi.kt index 0cd3e186..12e09d93 100644 --- a/features/ui-detail/src/main/java/com/pluu/webtoon/detail/ui/compose/DetailContentUi.kt +++ b/features/ui-detail/src/main/java/com/pluu/webtoon/detail/ui/compose/DetailContentUi.kt @@ -21,9 +21,8 @@ import androidx.compose.ui.unit.dp import com.pluu.webtoon.model.DetailView import com.pluu.webtoon.ui.compose.theme.themeRed import com.pluu.webtoon.ui_common.R -import com.pluu.webtoon.utils.glideUrl -import com.skydoves.landscapist.glide.GlideImage -import com.skydoves.landscapist.glide.GlideImageState +import com.pluu.webtoon.utils.ToonImage +import com.skydoves.landscapist.coil.CoilImageState @Composable internal fun DetailContentUi( @@ -67,8 +66,8 @@ private fun AdjustDetailImage( modifier: Modifier = Modifier, onSuccess: (DetailView, Size) -> Unit ) { - GlideImage( - imageModel = { item.url.glideUrl() }, + ToonImage( + imageUrl = { item.url }, modifier = modifier, loading = { CircularProgressIndicator( @@ -84,7 +83,7 @@ private fun AdjustDetailImage( ) }, onImageStateChanged = { - if (it is GlideImageState.Success) { + if (it is CoilImageState.Success) { val bitmap = it.imageBitmap!! onSuccess(item, Size(bitmap.width.toFloat(), bitmap.height.toFloat())) } diff --git a/features/ui-episode/src/main/java/com/pluu/webtoon/episode/ui/compose/EpisodeItemUi.kt b/features/ui-episode/src/main/java/com/pluu/webtoon/episode/ui/compose/EpisodeItemUi.kt index bf003707..0ac3d334 100644 --- a/features/ui-episode/src/main/java/com/pluu/webtoon/episode/ui/compose/EpisodeItemUi.kt +++ b/features/ui-episode/src/main/java/com/pluu/webtoon/episode/ui/compose/EpisodeItemUi.kt @@ -1,6 +1,5 @@ package com.pluu.webtoon.episode.ui.compose -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -9,18 +8,14 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.sizeIn -import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material3.Card -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -36,10 +31,7 @@ import com.pluu.webtoon.episode.R import com.pluu.webtoon.episode.compose.ImageInCircle import com.pluu.webtoon.model.EpisodeInfo import com.pluu.webtoon.ui.compose.theme.AppTheme -import com.pluu.webtoon.ui.compose.theme.themeRed -import com.pluu.webtoon.utils.glideUrl -import com.skydoves.landscapist.ImageOptions -import com.skydoves.landscapist.glide.GlideImage +import com.pluu.webtoon.utils.ToonImage @Composable internal fun EpisodeItemUi( @@ -48,6 +40,7 @@ internal fun EpisodeItemUi( isRead: Boolean, onClicked: (EpisodeInfo) -> Unit ) { + val context = LocalContext.current Card( modifier = modifier .fillMaxWidth() @@ -56,29 +49,10 @@ internal fun EpisodeItemUi( .clickable { onClicked(item) } ) { Box { - GlideImage( - imageModel = { item.image.glideUrl() }, + ToonImage( + imageUrl = { item.image }, modifier = Modifier.fillMaxSize(), - imageOptions = ImageOptions( - contentScale = ContentScale.Crop, - alignment = Alignment.Center - ), - previewPlaceholder = painterResource(id = com.pluu.compose.R.drawable.ic_baseline_android_24), - loading = { - CircularProgressIndicator( - modifier = Modifier - .matchParentSize() - .wrapContentSize(), - color = themeRed - ) - }, - failure = { - Image( - painter = painterResource(com.pluu.webtoon.ui_common.R.drawable.ic_sentiment_very_dissatisfied_48), - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface), - contentDescription = null - ) - } + previewPlaceholder = painterResource(id = com.pluu.compose.R.drawable.ic_baseline_android_24) ) EpisodeItemUiOverlayUi(item = item, isRead = isRead) } diff --git a/features/ui-weekly/src/main/java/com/pluu/webtoon/weekly/ui/day/WeeklyItemUi.kt b/features/ui-weekly/src/main/java/com/pluu/webtoon/weekly/ui/day/WeeklyItemUi.kt index 16bf1b3a..1bb4bd38 100644 --- a/features/ui-weekly/src/main/java/com/pluu/webtoon/weekly/ui/day/WeeklyItemUi.kt +++ b/features/ui-weekly/src/main/java/com/pluu/webtoon/weekly/ui/day/WeeklyItemUi.kt @@ -9,11 +9,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -23,7 +21,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -41,10 +38,8 @@ import com.pluu.webtoon.model.ToonInfo import com.pluu.webtoon.model.ToonInfoWithFavorite import com.pluu.webtoon.ui.compose.theme.AppTheme import com.pluu.webtoon.ui.compose.theme.themeRed -import com.pluu.webtoon.utils.glideUrl +import com.pluu.webtoon.utils.ToonImage import com.pluu.webtoon.weekly.R -import com.skydoves.landscapist.ImageOptions -import com.skydoves.landscapist.glide.GlideImage @Composable internal fun WeeklyItemUi( @@ -62,31 +57,12 @@ internal fun WeeklyItemUi( .clickable { onClicked(item) } ) { Box(modifier = Modifier.fillMaxSize()) { - GlideImage( - imageModel = { - item.image.glideUrl() - }, + ToonImage( + imageUrl = { item.image }, modifier = Modifier.fillMaxSize(), - imageOptions = ImageOptions( - contentScale = ContentScale.Crop, - alignment = Alignment.Center - ), - previewPlaceholder = painterResource(id = com.pluu.compose.R.drawable.ic_baseline_android_24), - loading = { - CircularProgressIndicator( - modifier = Modifier - .matchParentSize() - .wrapContentSize(), - color = themeRed - ) - }, - failure = { - Image( - modifier = Modifier.align(Alignment.Center), - painter = painterResource(com.pluu.webtoon.ui_common.R.drawable.ic_sentiment_very_dissatisfied_48), - contentDescription = null - ) - } + previewPlaceholder = painterResource( + id = com.pluu.compose.R.drawable.ic_baseline_android_24 + ) ) WeeklyItemOverlayUi( item = item, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c507d31d..f0953d7a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -66,7 +66,7 @@ kotlin-coroutine-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines- kotlin-coroutine-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlin-coroutine" } kotlin-serialization = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3" ## etc -landscapist = "com.github.skydoves:landscapist-glide:2.4.2" +landscapist = "com.github.skydoves:landscapist-coil:2.4.2" jsoup = "org.jsoup:jsoup:1.18.1" okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } okhttp-loggingInterceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }