diff --git a/app/src/main/java/org/thoughtcrime/securesms/compose/GlideImage.kt b/app/src/main/java/org/thoughtcrime/securesms/compose/GlideImage.kt index 1cde8c96a6..8a9422b049 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/compose/GlideImage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/compose/GlideImage.kt @@ -5,8 +5,8 @@ package org.thoughtcrime.securesms.compose -import android.graphics.Bitmap import android.graphics.drawable.Drawable +import android.widget.ImageView import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -16,41 +16,97 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.viewinterop.AndroidView import com.bumptech.glide.Glide +import com.bumptech.glide.TransitionOptions import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition +import com.google.accompanist.drawablepainter.rememberDrawablePainter +import org.thoughtcrime.securesms.glide.cache.ApngOptions /** - * Our very own GlideImage. + * Our very own GlideImage. The GlideImage composable provided by the bumptech library is not suitable because it was is using our encrypted cache decoder/encoder. */ @Composable fun GlideImage( - model: T?, modifier: Modifier = Modifier, + model: T?, imageSize: DpSize? = null, + scaleType: GlideImageScaleType = GlideImageScaleType.FIT_CENTER, fallback: Drawable? = null, error: Drawable? = fallback, + transition: TransitionOptions<*, Drawable>? = null, + diskCacheStrategy: DiskCacheStrategy = DiskCacheStrategy.ALL, + enableApngAnimation: Boolean = false +) { + if (enableApngAnimation) { + val density = LocalDensity.current + + AndroidView( + factory = { context -> ImageView(context) }, + update = { imageView -> + Glide.with(imageView.context) + .load(model) + .fallback(fallback) + .error(error) + .diskCacheStrategy(diskCacheStrategy) + .set(ApngOptions.ANIMATE, enableApngAnimation) + .apply { + scaleType.applyTo(this) + transition?.let(this::transition) + + if (imageSize != null) { + with(density) { + this@apply.override(imageSize.width.toPx().toInt(), imageSize.height.toPx().toInt()) + } + } + } + .into(imageView) + }, + modifier = modifier + ) + } else { + GlideImage( + model = model, + imageSize = imageSize, + scaleType = scaleType, + fallback = fallback, + error = error, + transition = transition, + diskCacheStrategy = diskCacheStrategy, + modifier = modifier + ) + } +} + +@Composable +private fun GlideImage( + modifier: Modifier = Modifier, + model: T?, + imageSize: DpSize? = null, + scaleType: GlideImageScaleType = GlideImageScaleType.FIT_CENTER, + fallback: Drawable? = null, + error: Drawable? = fallback, + transition: TransitionOptions<*, Drawable>? = null, diskCacheStrategy: DiskCacheStrategy = DiskCacheStrategy.ALL ) { - var bitmap by remember { - mutableStateOf(null) + var drawable by remember { + mutableStateOf(null) } val target = remember { - object : CustomTarget() { - override fun onResourceReady(resource: Bitmap, transition: Transition?) { - bitmap = resource.asImageBitmap() + object : CustomTarget() { + override fun onResourceReady(resource: Drawable, transition: Transition?) { + drawable = resource } override fun onLoadCleared(placeholder: Drawable?) { - bitmap = null + drawable = null } } } @@ -59,12 +115,14 @@ fun GlideImage( val context = LocalContext.current DisposableEffect(model, fallback, error, diskCacheStrategy, density, imageSize) { val builder = Glide.with(context) - .asBitmap() .load(model) .fallback(fallback) .error(error) .diskCacheStrategy(diskCacheStrategy) - .fitCenter() + .apply { + scaleType.applyTo(this) + transition?.let(this::transition) + } if (imageSize != null) { with(density) { @@ -77,18 +135,40 @@ fun GlideImage( object : DisposableEffectResult { override fun dispose() { Glide.with(context).clear(target) - bitmap = null + drawable = null } } } - val bm = bitmap - if (bm != null) { + if (drawable != null) { Image( - bitmap = bm, + painter = rememberDrawablePainter(drawable), contentDescription = null, contentScale = if (model == null) ContentScale.Inside else ContentScale.Crop, modifier = modifier ) } } + +enum class GlideImageScaleType { + /** @see [com.bumptech.glide.request.RequestOptions.fitCenter] */ + FIT_CENTER, + + /** @see [com.bumptech.glide.request.RequestOptions.centerInside] */ + CENTER_INSIDE, + + /** @see [com.bumptech.glide.request.RequestOptions.centerCrop] */ + CENTER_CROP, + + /** @see [com.bumptech.glide.request.RequestOptions.circleCrop] */ + CIRCLE_CROP; + + fun applyTo(builder: com.bumptech.glide.RequestBuilder): com.bumptech.glide.RequestBuilder { + return when (this) { + FIT_CENTER -> builder.fitCenter() + CENTER_INSIDE -> builder.centerInside() + CENTER_CROP -> builder.centerCrop() + CIRCLE_CROP -> builder.circleCrop() + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackListItems.kt b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackListItems.kt index ecffeb1874..1ad9540f9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackListItems.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackListItems.kt @@ -27,6 +27,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp @@ -42,6 +43,7 @@ import org.thoughtcrime.securesms.components.transfercontrols.TransferProgressSt import org.thoughtcrime.securesms.compose.GlideImage import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri import org.thoughtcrime.securesms.stickers.AvailableStickerPack.DownloadStatus +import org.thoughtcrime.securesms.util.DeviceProperties @Composable fun StickerPackSectionHeader( @@ -238,6 +240,7 @@ private fun StickerPackInfo( ) { GlideImage( model = coverImageUri, + enableApngAnimation = DeviceProperties.shouldAllowApngStickerAnimation(LocalContext.current), modifier = Modifier .padding(end = 16.dp) .size(56.dp)