|
|
|
|
@ -75,6 +75,8 @@ class ChatViewModel : ViewModel() {
|
|
|
|
|
|
|
|
|
|
private val _radioTalking = MutableStateFlow<Long?>(null) |
|
|
|
|
val radioTalking: StateFlow<Long?> = _radioTalking |
|
|
|
|
private val _radioTalkState = MutableStateFlow(RadioTalkState()) |
|
|
|
|
val radioTalkState: StateFlow<RadioTalkState> = _radioTalkState |
|
|
|
|
|
|
|
|
|
private val _radioRoute = MutableStateFlow(AudioRoute.SPEAKER) |
|
|
|
|
val radioRoute: StateFlow<AudioRoute> = _radioRoute |
|
|
|
|
@ -402,6 +404,11 @@ class ChatViewModel : ViewModel() {
|
|
|
|
|
4 -> { if (repo != null) { LogManager.addLog("INFO", "VM", "CHANNEL_UPDATED — refreshChannels"); refreshChannels() } else { LogManager.addLog("WARN", "VM", "CHANNEL_UPDATED skipped — repo is NULL") } } |
|
|
|
|
12 -> LogManager.addLog("INFO", "VM", "service started") |
|
|
|
|
13 -> { |
|
|
|
|
_radioTalkState.value = RadioTalkState() |
|
|
|
|
_radioTalking.value = null |
|
|
|
|
_radioActive.value = false |
|
|
|
|
radioChannelId = null |
|
|
|
|
radioAudio.stop() |
|
|
|
|
memberRadios.clear() |
|
|
|
|
memberRadioMutes.clear() |
|
|
|
|
_members.value = _members.value.map { it.copy(radioActive = false) } |
|
|
|
|
@ -567,6 +574,10 @@ class ChatViewModel : ViewModel() {
|
|
|
|
|
val chId = String(data, 1, cl) |
|
|
|
|
memberRadios[chId] = memberRadios[chId].orEmpty() - nodeId |
|
|
|
|
memberRadioMutes[chId] = memberRadioMutes[chId].orEmpty() - nodeId |
|
|
|
|
if (_radioTalkState.value.groupId == chId.toULongOrNull()?.toLong()) { |
|
|
|
|
_radioTalkState.value = _radioTalkState.value.copy(speakers = _radioTalkState.value.speakers - nodeId) |
|
|
|
|
_radioTalking.value = if (_radioActive.value) _radioTalkState.value.speakers.firstOrNull() else null |
|
|
|
|
} |
|
|
|
|
val cur = _currentChannel.value |
|
|
|
|
if (cur != null && cur.id == String(data, 1, cl)) |
|
|
|
|
_members.value = _members.value.filter { it.nodeId != nodeId } |
|
|
|
|
@ -736,17 +747,19 @@ class ChatViewModel : ViewModel() {
|
|
|
|
|
preparationState(target, job, state) |
|
|
|
|
} |
|
|
|
|
43 -> { /* RADIO_TALK: [group_id:8][src:8][stream:2][on:1] */ |
|
|
|
|
if (data == null || data.size < 19) return |
|
|
|
|
val bb = java.nio.ByteBuffer.wrap(data, 0, 19).order(java.nio.ByteOrder.LITTLE_ENDIAN) |
|
|
|
|
val groupId = bb.getLong() |
|
|
|
|
val src = bb.getLong() |
|
|
|
|
bb.getShort() /* stream */ |
|
|
|
|
val on = bb.get() |
|
|
|
|
val cur = _currentChannel.value |
|
|
|
|
if (cur != null && cur.id.toULongOrNull()?.toLong() == groupId) { |
|
|
|
|
_radioTalking.value = if (on.toInt() != 0) src else null |
|
|
|
|
LogManager.addLog("INFO", "VM", "RADIO_TALK ${if (on.toInt() != 0) "start" else "stop"} src=0x%016x".format(src)) |
|
|
|
|
val event = parseRadioTalkEvent(data) |
|
|
|
|
if (event == null) { LogManager.addLog("ERROR", "radio", "invalid RADIO_TALK bytes=${data?.size ?: 0}"); return } |
|
|
|
|
val previous = _radioTalkState.value |
|
|
|
|
val next = previous.onTalk(event) |
|
|
|
|
if (event.groupId != previous.groupId) { |
|
|
|
|
LogManager.addLog("DEBUG", "radio", "ignored talk group=${event.groupId} active=${previous.groupId} node=${event.nodeId}") |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
if (next == previous) return |
|
|
|
|
_radioTalkState.value = next |
|
|
|
|
_radioTalking.value = if (_radioActive.value) next.speakers.firstOrNull() else null |
|
|
|
|
LogManager.addLog("DEBUG", "radio", "talk group=${event.groupId} node=${event.nodeId} stream=${event.stream} " + |
|
|
|
|
"on=${event.on} speakers=${next.speakers.size}") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -776,6 +789,8 @@ class ChatViewModel : ViewModel() {
|
|
|
|
|
radioChannelId?.takeIf { it != ch.id }?.toULongOrNull()?.toLong() |
|
|
|
|
?.let { NativeLib.radioSetActive(it, false) } /* выключаем старый канал */ |
|
|
|
|
radioChannelId = ch.id |
|
|
|
|
_radioTalkState.value = RadioTalkState(groupId) |
|
|
|
|
_radioTalking.value = null |
|
|
|
|
NativeLib.radioSetActive(groupId, true) |
|
|
|
|
radioAudio.start(groupId) |
|
|
|
|
radioAudio.router.onChanged = { r, headset -> |
|
|
|
|
@ -797,6 +812,7 @@ class ChatViewModel : ViewModel() {
|
|
|
|
|
NativeLib.radioSetActive(groupId, false) |
|
|
|
|
radioAudio.stop() |
|
|
|
|
_radioTalking.value = null |
|
|
|
|
_radioTalkState.value = RadioTalkState() |
|
|
|
|
_radioRoute.value = AudioRoute.SPEAKER |
|
|
|
|
_radioHeadsetAvailable.value = false |
|
|
|
|
_wiredInputAvailable.value = false |
|
|
|
|
@ -908,6 +924,7 @@ class ChatViewModel : ViewModel() {
|
|
|
|
|
radioChannelId = null |
|
|
|
|
_radioActive.value = false |
|
|
|
|
_radioTalking.value = null |
|
|
|
|
_radioTalkState.value = RadioTalkState() |
|
|
|
|
_radioRoute.value = AudioRoute.SPEAKER |
|
|
|
|
_radioHeadsetAvailable.value = false |
|
|
|
|
_wiredInputAvailable.value = false |
|
|
|
|
@ -1031,7 +1048,7 @@ class ChatViewModel : ViewModel() {
|
|
|
|
|
} |
|
|
|
|
_currentChannel.value = channel |
|
|
|
|
_radioActive.value = (radioChannelId == channel.id) |
|
|
|
|
if (radioChannelId != channel.id) _radioTalking.value = null |
|
|
|
|
_radioTalking.value = if (_radioActive.value) _radioTalkState.value.speakers.firstOrNull() else null |
|
|
|
|
_soundOn.value = NativeLib.getSoundOnMessage(channel.id) |
|
|
|
|
NotificationSound.setMuted(channel.id, !_soundOn.value) |
|
|
|
|
r.connectChannel(channel.id) |
|
|
|
|
|