Move GlideImage into the glide module.

This commit is contained in:
Greyson Parrelli
2026-01-22 16:12:04 -05:00
committed by Alex Hart
parent 2f6baf8743
commit 986923ea6c
21 changed files with 42 additions and 28 deletions

View File

@@ -52,13 +52,13 @@ import androidx.compose.ui.viewinterop.AndroidView
import org.signal.core.ui.compose.Buttons
import org.signal.core.ui.compose.NightPreview
import org.signal.core.ui.compose.Previews
import org.signal.glide.compose.GlideImage
import org.signal.glide.compose.GlideImageScaleType
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.avatar.AvatarImage
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
import org.thoughtcrime.securesms.components.settings.app.subscription.BadgeImageLarge
import org.thoughtcrime.securesms.components.webrtc.TextureViewRenderer
import org.thoughtcrime.securesms.compose.GlideImage
import org.thoughtcrime.securesms.compose.GlideImageScaleType
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto
import org.thoughtcrime.securesms.events.CallParticipant
import org.thoughtcrime.securesms.recipients.Recipient

View File

@@ -37,9 +37,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.signal.core.ui.compose.NightPreview
import org.signal.core.ui.compose.Previews
import org.signal.glide.compose.GlideImage
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.avatar.AvatarImage
import org.thoughtcrime.securesms.compose.GlideImage
import org.thoughtcrime.securesms.recipients.Recipient
private val textShadow = Shadow(

View File

@@ -1,175 +0,0 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.compose
import android.graphics.drawable.Drawable
import android.widget.ImageView
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.DisposableEffectResult
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
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. The GlideImage composable provided by the bumptech library is not suitable because it was is using our encrypted cache decoder/encoder.
*/
@Composable
fun <T> 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,
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 <T> 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 drawable by remember {
mutableStateOf<Drawable?>(null)
}
val target = remember {
object : CustomTarget<Drawable>() {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
drawable = resource
}
override fun onLoadCleared(placeholder: Drawable?) {
drawable = null
}
}
}
val density = LocalDensity.current
val context = LocalContext.current
DisposableEffect(model, fallback, error, diskCacheStrategy, density, imageSize) {
val requestManager = Glide.with(context)
val builder = requestManager
.load(model)
.fallback(fallback)
.error(error)
.diskCacheStrategy(diskCacheStrategy)
.apply {
scaleType.applyTo(this)
transition?.let(this::transition)
}
if (imageSize != null) {
with(density) {
builder.override(imageSize.width.toPx().toInt(), imageSize.height.toPx().toInt()).into(target)
}
} else {
builder.into(target)
}
object : DisposableEffectResult {
override fun dispose() {
requestManager.clear(target)
drawable = null
}
}
}
if (drawable != null) {
Image(
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 <TranscodeT> applyTo(builder: com.bumptech.glide.RequestBuilder<TranscodeT>): com.bumptech.glide.RequestBuilder<TranscodeT> {
return when (this) {
FIT_CENTER -> builder.fitCenter()
CENTER_INSIDE -> builder.centerInside()
CENTER_CROP -> builder.centerCrop()
CIRCLE_CROP -> builder.circleCrop()
}
}
}

View File

@@ -48,9 +48,9 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.doOnPreDraw
import org.signal.core.ui.compose.DropdownMenus
import org.signal.core.ui.compose.theme.SignalTheme
import org.signal.glide.compose.GlideImage
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
import org.thoughtcrime.securesms.compose.GlideImage
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.fonts.SignalSymbols

View File

@@ -1,20 +0,0 @@
package org.thoughtcrime.securesms.glide.cache;
import com.bumptech.glide.load.Option;
import org.signal.core.util.Conversions;
/**
* Holds options that can be used to alter how APNGs are decoded in Glide.
*/
public final class ApngOptions {
private static final String KEY = "org.signal.skip_apng";
public static Option<Boolean> ANIMATE = Option.disk(KEY, true, (keyBytes, value, messageDigest) -> {
messageDigest.update(keyBytes);
messageDigest.update(Conversions.intToByteArray(value ? 1 : 0));
});
private ApngOptions() {}
}

View File

@@ -7,6 +7,7 @@ import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.engine.Resource;
import org.signal.glide.apng.ApngOptions;
import org.signal.glide.common.io.ByteBufferReader;
import org.signal.glide.common.loader.ByteBufferLoader;
import org.signal.glide.common.loader.Loader;

View File

@@ -8,6 +8,7 @@ import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.engine.Resource;
import org.signal.core.util.StreamUtil;
import org.signal.glide.apng.ApngOptions;
import org.signal.glide.common.io.StreamReader;
import org.signal.glide.load.resource.apng.decode.APNGDecoder;
import org.signal.glide.load.resource.apng.decode.APNGParser;

View File

@@ -12,6 +12,7 @@ import com.bumptech.glide.load.Options
import com.bumptech.glide.load.ResourceDecoder
import com.bumptech.glide.load.engine.Resource
import org.signal.core.util.StreamUtil
import org.signal.glide.apng.ApngOptions
import org.signal.glide.common.io.InputStreamFactory
import org.signal.glide.load.ImageHeaderParserUtils
import org.signal.glide.load.resource.apng.decode.APNGDecoder

View File

@@ -6,9 +6,9 @@ import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import org.signal.glide.apng.ApngOptions
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.database.model.StickerRecord
import org.thoughtcrime.securesms.glide.cache.ApngOptions
import org.thoughtcrime.securesms.mms.DecryptableUri
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter

View File

@@ -5,8 +5,8 @@ import android.view.View
import android.widget.ImageView
import androidx.core.widget.ImageViewCompat
import com.bumptech.glide.RequestManager
import org.signal.glide.apng.ApngOptions
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.glide.cache.ApngOptions
import org.thoughtcrime.securesms.mms.DecryptableUri
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter

View File

@@ -36,11 +36,11 @@ import org.signal.core.ui.compose.DropdownMenus
import org.signal.core.ui.compose.Previews
import org.signal.core.ui.compose.theme.SignalTheme
import org.signal.core.util.nullIfBlank
import org.signal.glide.compose.GlideImage
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.compose.RoundCheckbox
import org.thoughtcrime.securesms.components.transfercontrols.TransferProgressIndicator
import org.thoughtcrime.securesms.components.transfercontrols.TransferProgressState
import org.thoughtcrime.securesms.compose.GlideImage
import org.thoughtcrime.securesms.mms.DecryptableUri
import org.thoughtcrime.securesms.stickers.StickerPreviewDataFactory
import org.thoughtcrime.securesms.stickers.manage.AvailableStickerPack.DownloadStatus

View File

@@ -24,7 +24,7 @@ import org.thoughtcrime.securesms.PassphraseRequiredActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs;
import org.thoughtcrime.securesms.glide.cache.ApngOptions;
import org.signal.glide.apng.ApngOptions;
import org.thoughtcrime.securesms.mms.DecryptableUri;
import org.thoughtcrime.securesms.sharing.MultiShareArgs;
import org.thoughtcrime.securesms.stickers.StickerManifest;

View File

@@ -54,9 +54,9 @@ import org.signal.core.ui.compose.Previews
import org.signal.core.ui.compose.Scaffolds
import org.signal.core.util.orNull
import org.signal.core.util.toOptional
import org.signal.glide.compose.GlideImage
import org.thoughtcrime.securesms.PassphraseRequiredActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.compose.GlideImage
import org.thoughtcrime.securesms.compose.SignalTheme
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs

View File

@@ -13,7 +13,7 @@ import com.bumptech.glide.RequestManager;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.glide.cache.ApngOptions;
import org.signal.glide.apng.ApngOptions;
import org.thoughtcrime.securesms.mms.DecryptableUri;
import org.thoughtcrime.securesms.stickers.StickerManifest;
import org.thoughtcrime.securesms.stickers.StickerRemoteUri;