From 37b80b612b4f1901a22ad48257f0f1d137eebb0d Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 19:55:20 +0200 Subject: [PATCH] Restore Android gallery selections and validate editor source formats --- .../java/com/utun/chat/ui/components/AvatarCropDialog.kt | 3 +++ .../main/java/com/utun/chat/ui/screens/AvatarViewerScreen.kt | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/AvatarCropDialog.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/AvatarCropDialog.kt index 166c77ee..afd930f1 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/AvatarCropDialog.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/components/AvatarCropDialog.kt @@ -47,6 +47,9 @@ fun AvatarCropDialog(uri: Uri, onCancel: () -> Unit, onConfirm: (File, AvatarCro try { val image=withContext(Dispatchers.IO) { val file=AvatarAssets.copySource(context,uri); copied=file + val format=android.graphics.BitmapFactory.Options().apply { inJustDecodeBounds=true } + android.graphics.BitmapFactory.decodeFile(file.path,format) + require(format.outMimeType in listOf("image/jpeg","image/png","image/webp")) { "unsupported source format=${format.outMimeType}" } val (w,h)=PhotoUtil.orientedDimensions(file.path) require(w>0 && h>0 && w.toLong()*h<=16L*1024*1024) { "original exceeds 16 megapixels" } var sample=1; while (maxOf(w,h)/sample>1600) sample*=2 diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/AvatarViewerScreen.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/AvatarViewerScreen.kt index 3b350046..bf7ca9bb 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/AvatarViewerScreen.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/AvatarViewerScreen.kt @@ -34,6 +34,7 @@ fun AvatarViewerScreen(group: String?, owner: Long, name: String, onClose: () -> val source=remember(members,group,owner) { repository.gallerySource(group,owner) } var files by remember(group,owner,account) { mutableStateOf(emptyList()) } var selected by rememberSaveable(group,owner,account) { mutableStateOf("") } + var sourceKey by rememberSaveable(group,owner,account) { mutableStateOf("") } var previews by remember(group,owner,account) { mutableStateOf(emptyMap()) } var original by remember { mutableStateOf(null) } var failed by remember { mutableStateOf(false) } @@ -42,6 +43,8 @@ fun AvatarViewerScreen(group: String?, owner: Long, name: String, onClose: () -> LaunchedEffect(account,revision) { if (account==0L) onClose() } LaunchedEffect(source?.first,source?.second?.avatarServer,source?.second?.avatarFile,retry) { val (channel,member)=source ?: return@LaunchedEffect + val key="$channel:${member.nodeId}:${member.avatarServer}" + if (sourceKey!=key) { sourceKey=key; files=emptyList(); selected=member.avatarFile; previews=emptyMap() } if (selected.isEmpty()) selected=member.avatarFile if (files.isEmpty()) files=listOf(member.avatarFile) catalogFailed=false @@ -54,7 +57,7 @@ fun AvatarViewerScreen(group: String?, owner: Long, name: String, onClose: () -> LaunchedEffect(source?.first,source?.second?.avatarServer,selected,retry) { original=null; failed=false val (channel,member)=source ?: return@LaunchedEffect - if (index<0) return@LaunchedEffect + if (selected.isEmpty()) return@LaunchedEffect coroutineScope { launch { try { previews=previews+(selected to repository.loadAvatar(channel,member.copy(avatarFile=selected),false)) }