Try fixing location pin previews (#6495)

* Try fixing location pin previews

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
Co-authored-by: Benoit Marty <benoitm@element.io>
This commit is contained in:
Jorge Martin Espinosa 2026-03-31 16:57:37 +02:00 committed by GitHub
parent 5c8bf44b1b
commit dbd51ebc90
5 changed files with 46 additions and 27 deletions

View file

@ -32,9 +32,13 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.createBitmap import androidx.core.graphics.createBitmap
import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.withSave import androidx.core.graphics.withSave
import coil3.Image import coil3.Image
import coil3.ImageLoader import coil3.ImageLoader
@ -50,6 +54,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.utils.CommonDrawables
private val PIN_WIDTH = 42.dp private val PIN_WIDTH = 42.dp
private val PIN_HEIGHT = PIN_WIDTH * 1.2f private val PIN_HEIGHT = PIN_WIDTH * 1.2f
@ -99,21 +104,33 @@ fun LocationPin(
fun rememberLocationPinBitmap(variant: PinVariant): ImageBitmap? { fun rememberLocationPinBitmap(variant: PinVariant): ImageBitmap? {
val context = LocalContext.current val context = LocalContext.current
val density = LocalDensity.current val density = LocalDensity.current
val imageLoader = SingletonImageLoader.get(context)
val colors = pinColors(variant) val colors = pinColors(variant)
val cacheKey = rememberCacheKey(variant) val cacheKey = rememberCacheKey(variant)
return produceState<ImageBitmap?>(initialValue = null, cacheKey) { val resources = LocalResources.current
val memoryCacheKey = MemoryCache.Key(cacheKey)
val cached = imageLoader.memoryCache?.get(memoryCacheKey) return if (LocalInspectionMode.current) {
if (cached != null) { // In preview mode, skip async loading and return a simple placeholder image instead to avoid using ImageLoader
value = cached.image.toBitmap().asImageBitmap() val dimensions = PinDimensions(density)
} else { val avatarImage = ResourcesCompat.getDrawable(resources, CommonDrawables.sample_avatar, context.theme)?.toBitmap()?.asImage()
val dimensions = PinDimensions(density) LocationPinRenderer.renderPin(variant, colors, dimensions, avatarImage).asImageBitmap()
val bitmap = LocationPinRenderer.renderPin(variant, colors, dimensions, context, imageLoader) } else {
imageLoader.memoryCache?.set(memoryCacheKey, MemoryCache.Value(bitmap.asImage())) produceState<ImageBitmap?>(initialValue = null, cacheKey) {
value = bitmap.asImageBitmap() val imageLoader = SingletonImageLoader.get(context)
} val memoryCacheKey = MemoryCache.Key(cacheKey)
}.value val cached = imageLoader.memoryCache?.get(memoryCacheKey)
if (cached != null) {
value = cached.image.toBitmap().asImageBitmap()
} else {
val dimensions = PinDimensions(density)
val bitmap = with(LocationPinRenderer) {
val avatarImage = loadAvatarImage(variant, context, imageLoader)
renderPin(variant, colors, dimensions, avatarImage)
}
imageLoader.memoryCache?.set(memoryCacheKey, MemoryCache.Value(bitmap.asImage()))
value = bitmap.asImageBitmap()
}
}.value
}
} }
@Composable @Composable
@ -208,19 +225,17 @@ private object LocationPinRenderer {
/** /**
* Renders a pin variant to bitmap. Suspending for async avatar loading. * Renders a pin variant to bitmap. Suspending for async avatar loading.
*/ */
suspend fun renderPin( fun renderPin(
variant: PinVariant, variant: PinVariant,
colors: PinColors, colors: PinColors,
dimensions: PinDimensions, dimensions: PinDimensions,
context: Context, avatarImage: Image?,
imageLoader: ImageLoader,
): Bitmap { ): Bitmap {
val bitmap = createBitmap(dimensions.pinWidth.toInt(), dimensions.pinHeight.toInt()) val bitmap = createBitmap(dimensions.pinWidth.toInt(), dimensions.pinHeight.toInt())
val canvas = Canvas(bitmap) val canvas = Canvas(bitmap)
canvas.drawPinShape(colors.fill, colors.stroke, dimensions) canvas.drawPinShape(colors.fill, colors.stroke, dimensions)
when (variant) { when (variant) {
is PinVariant.UserLocation -> { is PinVariant.UserLocation -> {
val avatarImage = loadAvatarImage(variant.avatarData, context, imageLoader)
canvas.drawAvatar( canvas.drawAvatar(
avatarImage = avatarImage, avatarImage = avatarImage,
avatarData = variant.avatarData, avatarData = variant.avatarData,
@ -284,11 +299,15 @@ private object LocationPinRenderer {
return path return path
} }
private suspend fun loadAvatarImage( suspend fun loadAvatarImage(
avatarData: AvatarData, variant: PinVariant,
context: Context, context: Context,
imageLoader: ImageLoader, imageLoader: ImageLoader,
): Image? { ): Image? {
val avatarData = when (variant) {
is PinVariant.UserLocation -> variant.avatarData
else -> return null
}
val request = ImageRequest.Builder(context) val request = ImageRequest.Builder(context)
.data(avatarData) .data(avatarData)
// Disable hardware rendering for Canvas // Disable hardware rendering for Canvas

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:3ba1418b5d42a56db47e7cc574cedb886c75d9cf22828341bd954a4f1845670e oid sha256:2621fef4175ad0f0982270284ffbbdb6b3b0534b09013c1cc378504a85d13068
size 17925 size 22385

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d786937d790e13b53a5de8ae3321e5aa8744a979a3a99ecc36def6b7dbf6cf60 oid sha256:69bcf19161f70c27256c1bf0e5b99c993c86dd3e77a42d0e87061730a5c0c752
size 17237 size 21649

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:4d8c17c2e421452f57e4679111569054e1ed1bacc952cc1df4c7efe1f67c47ef oid sha256:ca6415ca7f858146a4c00e2fa1d9602fd9f3d42c0c8ba9830e121769af80676a
size 13158 size 17255

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9767298a096be7fc78210245e6b806a72a6c1ad16d7335e0f89728bbcf08ebd4 oid sha256:d89b754907f98ef1df2cf227ec535cdad91dc15e90b4915039be39d8b2ae2ebd
size 15884 size 16721