Browse Source

Update Android consumers for extended member snapshots

master
evgeny 6 days ago
parent
commit
ae461e746f
  1. 8
      doc/member_files.txt
  2. 16
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt
  3. 10
      tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt

8
doc/member_files.txt

@ -316,10 +316,10 @@ AvatarManager общий для списка участников и сообщ
test_avatars проверяет нормализацию, хеш/размеры, очистку temp, настоящее storage
в двух каналах, публикацию, сохранение при смене имени, DM-кеш, restart без сети,
автоматический download после потери кеша и удаление ссылок. test_member_adapter
Проверяет также max_bytes до открытия local temp и смену аватарки при задержанном
download старой версии: поздний результат не заменяет новый рисунок.
проверяет подписанные профили 191/255/256/512/1023/1024 байт вместе с adm_tags до 8192.
автоматический download после потери кеша и удаление ссылок. Также проверяются
max_bytes до открытия local temp и смена аватарки при задержанном download старой
версии: поздний результат не заменяет новый рисунок. test_member_adapter проверяет
подписанные профили 191/255/256/512/1023/1024 байт вместе с adm_tags до 8192.
Категория media=debug показывает discovery, cache hit/eviction, направление,
group/owner/server/hash, ошибки проверки и retry; media=info — передачи/публикации.

16
tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt

@ -161,16 +161,18 @@ fun parseConnList(data: ByteArray): List<ConnInfo> {
return list
}
/* ── Member wire format (79 bytes, from chat_member.h) ── */
/* Размер общего снимка из chat_member.h; одинаковый для desktop и Android. */
const val MEMBER_WIRE_SIZE = 157
data class MemberWire(
val nodeId: Long, val online: Int, val isSelf: Boolean,
val flags: Int, val rtt: Int, val natType: Int,
val storage: Boolean, val name: String
val storage: Boolean, val name: String,
val avatarServer: Long = 0, val avatarFile: String = "", val storageRole: Boolean = false
)
fun parseMemberWire(data: ByteArray, offset: Int = 0): MemberWire? {
if (offset < 0 || offset > data.size - 79) return null
val buf = ByteBuffer.wrap(data, offset, 79).order(ByteOrder.LITTLE_ENDIAN)
if (offset < 0 || offset > data.size - MEMBER_WIRE_SIZE) return null
val buf = ByteBuffer.wrap(data, offset, MEMBER_WIRE_SIZE).order(ByteOrder.LITTLE_ENDIAN)
val nodeId = buf.getLong()
val online = buf.get().toInt() and 0xFF
val isSelf = buf.get().toInt() != 0
@ -180,7 +182,11 @@ fun parseMemberWire(data: ByteArray, offset: Int = 0): MemberWire? {
val storage = buf.get().toInt() != 0
val nameBytes = ByteArray(64); buf.get(nameBytes)
val name = String(nameBytes).trimEnd('\u0000')
return MemberWire(nodeId, online, isSelf, flags, rtt, natType, storage, name)
val avatarServer = buf.getLong()
val avatarBytes = ByteArray(69); buf.get(avatarBytes)
val avatarFile = String(avatarBytes).trimEnd('\u0000')
val storageRole = buf.get().toInt() != 0
return MemberWire(nodeId, online, isSelf, flags, rtt, natType, storage, name, avatarServer, avatarFile, storageRole)
}
/* Wire не содержит verified/nodeType и состояния транспортов: сохраняем их из текущей строки. */

10
tools/chatgui-android/app/src/main/java/com/utun/chat/viewmodel/ChatViewModel.kt

@ -467,11 +467,11 @@ class ChatViewModel : ViewModel() {
val chId = String(data, 1, cl)
val count = (data[1 + cl].toInt() and 0xFF) or ((data[2 + cl].toInt() and 0xFF) shl 8)
val offset = cl + 3
if (data.size != offset + count * 79) {
if (data.size != offset + count * MEMBER_WIRE_SIZE) {
LogManager.addLog("ERROR", "VM", "MEMBER_LIST: invalid size ch=$chId count=$count bytes=${data.size}"); return
}
val radios = (0 until count).associate { i ->
val w = requireNotNull(parseMemberWire(data, offset + i * 79))
val w = requireNotNull(parseMemberWire(data, offset + i * MEMBER_WIRE_SIZE))
w.nodeId to ((w.flags and 0x10) != 0)
}
memberRadios[chId] = radios
@ -479,12 +479,12 @@ class ChatViewModel : ViewModel() {
_members.value = _members.value.map { it.copy(radioActive = radios[it.nodeId] == true) }
LogManager.addLog("DEBUG", "VM", "MEMBER_LIST: ch=$chId members=$count radios=${radios.values.count { it }}")
}
24 -> { /* MEMBER_UPDATED: [ch_id_len:1][ch_id:var][count:1][member:79B] */
if (data == null || data.size < 3 + 79) {
24 -> { /* MEMBER_UPDATED: [ch_id_len:1][ch_id:var][count:1][member:MEMBER_WIRE_SIZE] */
if (data == null || data.size < 3 + MEMBER_WIRE_SIZE) {
LogManager.addLog("ERROR", "VM", "MEMBER_UPDATED: missing header/member"); return
}
val cl = data[0].toInt() and 0xFF
if (cl == 0 || data.size != cl + 2 + 79 || data[cl + 1].toInt() != 1) {
if (cl == 0 || data.size != cl + 2 + MEMBER_WIRE_SIZE || data[cl + 1].toInt() != 1) {
LogManager.addLog("ERROR", "VM", "MEMBER_UPDATED: invalid payload channelLength=$cl bytes=${data.size}"); return
}
val chId = String(data, 1, cl)

Loading…
Cancel
Save