fix: fix image flashing

This commit is contained in:
k1ngsterr1
2026-02-04 01:03:56 +05:00
parent 9d7c016b70
commit 659f0c097d

View File

@@ -4,6 +4,7 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.util.Base64
import android.util.LruCache
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
@@ -55,6 +56,40 @@ import kotlin.math.min
private const val TAG = "AttachmentComponents"
/**
* 🖼️ Глобальный LRU кэш для bitmap'ов изображений
* Это предотвращает мигание при перезаходе в диалог,
* так как изображения остаются в памяти между recomposition
*/
object ImageBitmapCache {
// Размер кэша = 1/8 доступной памяти (стандартная практика Android)
private val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()
private val cacheSize = maxMemory / 8
private val cache = object : LruCache<String, Bitmap>(cacheSize) {
override fun sizeOf(key: String, bitmap: Bitmap): Int {
// Размер в килобайтах
return bitmap.byteCount / 1024
}
}
fun get(key: String): Bitmap? = cache.get(key)
fun put(key: String, bitmap: Bitmap) {
if (cache.get(key) == null) {
cache.put(key, bitmap)
}
}
fun remove(key: String) {
cache.remove(key)
}
fun clear() {
cache.evictAll()
}
}
/**
* 📐 Telegram Bubble Specification
* Все константы взяты из ChatMessageCell.java и Theme.java
@@ -523,10 +558,21 @@ fun ImageAttachment(
// Bounds для shared element transition
var imageBounds by remember { mutableStateOf<Rect?>(null) }
var downloadStatus by remember { mutableStateOf(DownloadStatus.PENDING) }
var imageBitmap by remember { mutableStateOf<Bitmap?>(null) }
var blurhashBitmap by remember { mutableStateOf<Bitmap?>(null) }
var downloadProgress by remember { mutableStateOf(0f) }
// 🔥 Ключ для глобального кэша
val cacheKey = "img_${attachment.id}"
// 🔥 Сначала проверяем глобальный кэш
var downloadStatus by remember(attachment.id) {
mutableStateOf(
if (ImageBitmapCache.get(cacheKey) != null) DownloadStatus.DOWNLOADED
else DownloadStatus.PENDING
)
}
var imageBitmap by remember(attachment.id) {
mutableStateOf(ImageBitmapCache.get(cacheKey))
}
var blurhashBitmap by remember(attachment.id) { mutableStateOf<Bitmap?>(null) }
var downloadProgress by remember(attachment.id) { mutableStateOf(0f) }
val preview = getPreview(attachment.preview)
val downloadTag = getDownloadTag(attachment.preview)
@@ -541,6 +587,19 @@ fun ImageAttachment(
// Определяем начальный статус и декодируем blurhash (как в Desktop calcDownloadStatus)
LaunchedEffect(attachment.id, attachment.localUri) {
// 🔥 Если изображение уже в глобальном кэше - используем его!
val cachedBitmap = ImageBitmapCache.get(cacheKey)
if (cachedBitmap != null) {
imageBitmap = cachedBitmap
downloadStatus = DownloadStatus.DOWNLOADED
return@LaunchedEffect
}
// 🔥 Если изображение уже загружено локально - не перезагружаем!
if (imageBitmap != null && downloadStatus == DownloadStatus.DOWNLOADED) {
return@LaunchedEffect
}
// Определяем статус (логика из Desktop useAttachment.ts)
withContext(Dispatchers.IO) {
downloadStatus =
@@ -594,7 +653,7 @@ fun ImageAttachment(
if (downloadStatus == DownloadStatus.DOWNLOADED) {
withContext(Dispatchers.IO) {
// 🚀 0. Если есть localUri - загружаем напрямую из URI (optimistic UI)
if (attachment.localUri.isNotEmpty()) {
if (imageBitmap == null && attachment.localUri.isNotEmpty()) {
try {
val uri = android.net.Uri.parse(attachment.localUri)
@@ -641,7 +700,11 @@ fun ImageAttachment(
}
}
imageBitmap = bitmap
if (bitmap != null) {
imageBitmap = bitmap
// 🔥 Сохраняем в глобальный кэш
ImageBitmapCache.put(cacheKey, bitmap)
}
} catch (e: Exception) {
// Fallback to other methods
}
@@ -649,7 +712,12 @@ fun ImageAttachment(
// 1. Если нет bitmap, пробуем blob из памяти
if (imageBitmap == null && attachment.blob.isNotEmpty()) {
imageBitmap = base64ToBitmap(attachment.blob)
val bitmap = base64ToBitmap(attachment.blob)
if (bitmap != null) {
imageBitmap = bitmap
// 🔥 Сохраняем в глобальный кэш
ImageBitmapCache.put(cacheKey, bitmap)
}
}
// 2. Если всё ещё нет, читаем из файловой системы
@@ -662,7 +730,12 @@ fun ImageAttachment(
privateKey
)
if (localBlob != null) {
imageBitmap = base64ToBitmap(localBlob)
val bitmap = base64ToBitmap(localBlob)
if (bitmap != null) {
imageBitmap = bitmap
// 🔥 Сохраняем в глобальный кэш
ImageBitmapCache.put(cacheKey, bitmap)
}
} else if (attachment.localUri.isEmpty()) {
// Только если нет localUri - помечаем как NOT_DOWNLOADED
downloadStatus = DownloadStatus.NOT_DOWNLOADED