Open the Android DM device panel and validate snapshot decoding
This commit is contained in:
1 parent
317ea6565f
commit
9e7366d5fc
3 files changed
+57
No files matched your search
@@ -10,6 +10,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Call
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Group
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
@@ -42,6 +43,8 @@ fun DmChatScreen(conv: DmConversation, messages: List<Message>, viewModel: ChatV
|
||||
onRecordVideo: () -> Unit, onPlayVideo: (Message) -> Unit,
|
||||
onCapturePhoto: () -> Unit, onViewPhoto: (Message) -> Unit) {
|
||||
val target = ConversationTarget.Pm(conv.convId, conv.generation)
|
||||
var showDevices by rememberSaveable(conv.convId, conv.generation) { mutableStateOf(false) }
|
||||
if (showDevices && conv.mode == 1) DmMembersSheet(conv) { showDevices = false }
|
||||
val playback by viewModel.playbackState.collectAsState()
|
||||
val callAvailable by viewModel.dmCallAvailable.collectAsState()
|
||||
val callState by viewModel.callState.collectAsState()
|
||||
@@ -118,6 +121,9 @@ fun DmChatScreen(conv: DmConversation, messages: List<Message>, viewModel: ChatV
|
||||
}, navigationIcon = {
|
||||
IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, stringResource(R.string.back)) }
|
||||
}, actions = {
|
||||
if (conv.mode == 1) IconButton(onClick = { showDevices = true }) {
|
||||
Icon(Icons.Filled.Group, stringResource(R.string.dm_members_title))
|
||||
}
|
||||
IconButton(onClick = { viewModel.showPmInvite(conv.peerNodeId, conv.displayName, target.nativeId) }, enabled = !conv.closed) {
|
||||
Icon(Icons.Filled.Add, stringResource(R.string.invite_to_group))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="dm_members_title">Устройства беседы</string>
|
||||
<string name="dm_my_devices">Мои устройства</string>
|
||||
<string name="dm_peer_devices">Устройства собеседника</string>
|
||||
<string name="dm_members_unavailable">Не удалось обновить список устройств</string>
|
||||
<string name="dm_mode_title">Устройства для этой беседы</string>
|
||||
<string name="dm_mode_groups">С подгруппами устройств</string>
|
||||
<string name="dm_mode_device">Только эти устройства</string>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.utun.chat.data
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
class DmMembersTest {
|
||||
private val target = ConversationTarget.Pm("42", 7)
|
||||
private fun frame(vararg entries: Triple<Int, Long, Boolean>): ByteArray {
|
||||
val data = ByteArray(13 + entries.size * (MEMBER_WIRE_SIZE + 1))
|
||||
data[0] = 2; data[1] = '4'.code.toByte(); data[2] = '2'.code.toByte()
|
||||
ByteBuffer.wrap(data, 3, 10).order(ByteOrder.LITTLE_ENDIAN).putLong(7).putShort(entries.size.toShort())
|
||||
entries.forEachIndexed { i, (side, node, self) ->
|
||||
val offset = 13 + i * (MEMBER_WIRE_SIZE + 1); data[offset] = side.toByte()
|
||||
ByteBuffer.wrap(data, offset + 1, 8).order(ByteOrder.LITTLE_ENDIAN).putLong(node)
|
||||
data[offset + 9] = if (self) 1 else 0; data[offset + 10] = if (self) 1 else 0
|
||||
"node$node".toByteArray().copyInto(data, offset + 16)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@Test fun separatesOwnAndPeerDevicesIncludingOffline() {
|
||||
val members = parseDmMembers(frame(Triple(1, 9L, false), Triple(0, 7L, false), Triple(0, 21L, true)), target)
|
||||
assertEquals(listOf(21L, 7L, 9L), members.map { it.member.nodeId })
|
||||
assertEquals(listOf(0, 0, 1), members.map { it.side })
|
||||
assertEquals(listOf(true, false, false), members.map { it.member.online })
|
||||
assertTrue(members.first().member.isSelf)
|
||||
}
|
||||
|
||||
@Test fun emptyOrFallbackSideDoesNotRequireAGroup() {
|
||||
assertTrue(parseDmMembers(frame(), target).isEmpty())
|
||||
val members = parseDmMembers(frame(Triple(1, 9L, false)), target)
|
||||
assertEquals(1, members.size); assertEquals("", members.single().member.devgroup)
|
||||
}
|
||||
|
||||
@Test fun rejectsOtherConversationGenerationAndMalformedRecords() {
|
||||
val data = frame(Triple(0, 7L, true), Triple(1, 9L, false))
|
||||
fun rejected(bytes: ByteArray, selected: ConversationTarget.Pm = target) {
|
||||
assertThrows(IllegalArgumentException::class.java) { parseDmMembers(bytes, selected) }
|
||||
}
|
||||
rejected(data, target.copy(id = "43")); rejected(data, target.copy(generation = 8))
|
||||
rejected(data.copyOf(data.size - 1)); rejected(data + 0.toByte())
|
||||
rejected(frame(Triple(0, 7L, true), Triple(1, 7L, false)))
|
||||
rejected(frame(Triple(2, 9L, false))); rejected(frame(Triple(1, 0L, false)))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user