fix: fix image flashing
This commit is contained in:
@@ -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(
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user