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 bc37d3d8d9
commit 579bc7db62
5 changed files with 46 additions and 27 deletions
@@ -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