Add group monitoring mode (v1.0.5)
- Monitor button between callsign and TG selector - On: reconnects with TG_MONITOR for all switch-list groups, status shows 'Мониторинг: TG...', PTT disabled - Talker display shows callsign and its TG - Off: back to normal talk mode
This commit is contained in:
@@ -9,6 +9,8 @@ import android.content.Intent
|
|||||||
import android.content.ServiceConnection
|
import android.content.ServiceConnection
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.Color
|
||||||
import android.graphics.PixelFormat
|
import android.graphics.PixelFormat
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@@ -57,10 +59,12 @@ class MainActivity : Activity(), SvxService.Listener {
|
|||||||
private lateinit var micDb: TextView
|
private lateinit var micDb: TextView
|
||||||
private lateinit var pttButton: PttButton
|
private lateinit var pttButton: PttButton
|
||||||
private lateinit var btnSettings: ImageButton
|
private lateinit var btnSettings: ImageButton
|
||||||
|
private lateinit var btnMonitor: ImageButton
|
||||||
|
|
||||||
private val mainHandler = Handler(Looper.getMainLooper())
|
private val mainHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
private var connected = false
|
private var connected = false
|
||||||
|
private var monitoring = false
|
||||||
private var settings: ConnectionSettings = ConnectionSettings("", 5300, "", "", 11)
|
private var settings: ConnectionSettings = ConnectionSettings("", 5300, "", "", 11)
|
||||||
|
|
||||||
private var svc: SvxService? = null
|
private var svc: SvxService? = null
|
||||||
@@ -101,8 +105,11 @@ class MainActivity : Activity(), SvxService.Listener {
|
|||||||
micDb = findViewById(R.id.mic_db)
|
micDb = findViewById(R.id.mic_db)
|
||||||
pttButton = findViewById(R.id.ptt_button)
|
pttButton = findViewById(R.id.ptt_button)
|
||||||
btnSettings = findViewById(R.id.btn_settings)
|
btnSettings = findViewById(R.id.btn_settings)
|
||||||
|
btnMonitor = findViewById(R.id.btn_monitor)
|
||||||
pttButton.enabled2 = false
|
pttButton.enabled2 = false
|
||||||
|
|
||||||
|
btnMonitor.setOnClickListener { toggleMonitoring() }
|
||||||
|
updateMonitorButton()
|
||||||
val testText = intent?.getStringExtra("node_toast_test")
|
val testText = intent?.getStringExtra("node_toast_test")
|
||||||
if (testText != null) {
|
if (testText != null) {
|
||||||
mainHandler.postDelayed({ showNodeOverlay(testText) }, 1500)
|
mainHandler.postDelayed({ showNodeOverlay(testText) }, 1500)
|
||||||
@@ -239,7 +246,11 @@ class MainActivity : Activity(), SvxService.Listener {
|
|||||||
ReflectorClient.STATE_READY -> {
|
ReflectorClient.STATE_READY -> {
|
||||||
connected = true
|
connected = true
|
||||||
updatePttEnabled()
|
updatePttEnabled()
|
||||||
statusChip.text = getString(R.string.status_connected)
|
statusChip.text = if (monitoring) {
|
||||||
|
getString(R.string.status_monitoring, monitorTgLabel())
|
||||||
|
} else {
|
||||||
|
getString(R.string.status_connected)
|
||||||
|
}
|
||||||
statusChip.setTextColor(getColor(R.color.accent))
|
statusChip.setTextColor(getColor(R.color.accent))
|
||||||
lblTxState.text = getString(R.string.status_big_connected)
|
lblTxState.text = getString(R.string.status_big_connected)
|
||||||
lblTxState.setTextColor(getColor(R.color.accent))
|
lblTxState.setTextColor(getColor(R.color.accent))
|
||||||
@@ -280,7 +291,11 @@ class MainActivity : Activity(), SvxService.Listener {
|
|||||||
|
|
||||||
override fun onTalkerStart(tg: Long, callsign: String) {
|
override fun onTalkerStart(tg: Long, callsign: String) {
|
||||||
mainHandler.post {
|
mainHandler.post {
|
||||||
lblTalkerTitle.text = getString(R.string.talker_now, callsign)
|
lblTalkerTitle.text = if (monitoring) {
|
||||||
|
getString(R.string.talker_mon, callsign, tg)
|
||||||
|
} else {
|
||||||
|
getString(R.string.talker_now, callsign)
|
||||||
|
}
|
||||||
lblTalker.text = callsign
|
lblTalker.text = callsign
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -366,7 +381,7 @@ class MainActivity : Activity(), SvxService.Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePttEnabled() {
|
private fun updatePttEnabled() {
|
||||||
pttButton.enabled2 = connected
|
pttButton.enabled2 = connected && !monitoring
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requestPermissionsIfNeeded() {
|
private fun requestPermissionsIfNeeded() {
|
||||||
@@ -391,12 +406,36 @@ class MainActivity : Activity(), SvxService.Listener {
|
|||||||
port = settings.port,
|
port = settings.port,
|
||||||
callsign = settings.callsign,
|
callsign = settings.callsign,
|
||||||
authKey = settings.authKey,
|
authKey = settings.authKey,
|
||||||
talkGroup = settings.talkGroup
|
talkGroup = settings.talkGroup,
|
||||||
|
monitorTgs = if (monitoring) settings.groups else null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Включить/выключить режим мониторинга групп с переподключением. */
|
||||||
|
private fun toggleMonitoring() {
|
||||||
|
monitoring = !monitoring
|
||||||
|
updateMonitorButton()
|
||||||
|
updatePttEnabled()
|
||||||
|
if (connected) {
|
||||||
|
svc?.disconnect()
|
||||||
|
mainHandler.postDelayed({ connect() }, 500)
|
||||||
|
} else {
|
||||||
|
connect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateMonitorButton() {
|
||||||
|
val active = monitoring
|
||||||
|
btnMonitor.imageTintList = ColorStateList.valueOf(
|
||||||
|
if (active) Color.parseColor("#58A6FF") else Color.parseColor("#8B949E")
|
||||||
|
)
|
||||||
|
btnMonitor.setBackgroundResource(if (active) R.drawable.bg_monitor_on else 0)
|
||||||
|
btnMonitor.contentDescription =
|
||||||
|
getString(if (active) R.string.btn_monitor_on else R.string.btn_monitor)
|
||||||
|
}
|
||||||
|
|
||||||
/** Переключиться на другую группу: пересохранить и переподключиться. */
|
/** Переключиться на другую группу: пересохранить и переподключиться. */
|
||||||
private fun switchGroup(tg: Long) {
|
private fun switchGroup(tg: Long) {
|
||||||
settings = settings.copy(talkGroup = tg)
|
settings = settings.copy(talkGroup = tg)
|
||||||
@@ -406,6 +445,9 @@ class MainActivity : Activity(), SvxService.Listener {
|
|||||||
mainHandler.postDelayed({ connect() }, 500)
|
mainHandler.postDelayed({ connect() }, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun monitorTgLabel(): String =
|
||||||
|
settings.groups.joinToString(", ") { "TG$it" }
|
||||||
|
|
||||||
private fun showNodesDialog() {
|
private fun showNodesDialog() {
|
||||||
val nodes = svc?.nodeList().orEmpty()
|
val nodes = svc?.nodeList().orEmpty()
|
||||||
if (nodes.isEmpty()) {
|
if (nodes.isEmpty()) {
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ class ReflectorClient(
|
|||||||
val port: Int,
|
val port: Int,
|
||||||
val callsign: String,
|
val callsign: String,
|
||||||
val authKey: String,
|
val authKey: String,
|
||||||
val talkGroup: Long
|
val talkGroup: Long,
|
||||||
|
/** Список групп для мониторинга; null = обычный режим (своя группа). */
|
||||||
|
val monitorTgs: List<Long>? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
@@ -361,8 +363,14 @@ class ReflectorClient(
|
|||||||
// (иначе сервер не шлёт аудио группы и не показывает
|
// (иначе сервер не шлёт аудио группы и не показывает
|
||||||
// подписку в статусе: monitoredTGs остаётся пустым).
|
// подписку в статусе: monitoredTGs остаётся пустым).
|
||||||
sendTcp(socket, MsgBuilder.nodeInfo(CLIENT_SW_INFO, CLIENT_SW_VERSION))
|
sendTcp(socket, MsgBuilder.nodeInfo(CLIENT_SW_INFO, CLIENT_SW_VERSION))
|
||||||
|
val monitor = s.monitorTgs
|
||||||
|
if (monitor.isNullOrEmpty()) {
|
||||||
sendTcp(socket, MsgBuilder.selectTg(s.talkGroup))
|
sendTcp(socket, MsgBuilder.selectTg(s.talkGroup))
|
||||||
sendTcp(socket, MsgBuilder.tgMonitor(listOf(s.talkGroup)))
|
sendTcp(socket, MsgBuilder.tgMonitor(listOf(s.talkGroup)))
|
||||||
|
} else {
|
||||||
|
sendTcp(socket, MsgBuilder.selectTg(0))
|
||||||
|
sendTcp(socket, MsgBuilder.tgMonitor(monitor))
|
||||||
|
}
|
||||||
openUdp(s)
|
openUdp(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
app/src/main/res/drawable/bg_monitor_on.xml
Normal file
5
app/src/main/res/drawable/bg_monitor_on.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="#3358A6FF" />
|
||||||
|
</shape>
|
||||||
10
app/src/main/res/drawable/ic_headset.xml
Normal file
10
app/src/main/res/drawable/ic_headset.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#8B949E"
|
||||||
|
android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8L5,12v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z" />
|
||||||
|
</vector>
|
||||||
@@ -84,6 +84,15 @@
|
|||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:text="@string/settings_not_configured" />
|
android:text="@string/settings_not_configured" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_monitor"
|
||||||
|
android:layout_width="44dp"
|
||||||
|
android:layout_height="44dp"
|
||||||
|
android:src="@drawable/ic_headset"
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/btn_monitor"
|
||||||
|
android:scaleType="centerInside" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/lbl_tg"
|
android:id="@+id/lbl_tg"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
<string name="status_disconnected">Отключено</string>
|
<string name="status_disconnected">Отключено</string>
|
||||||
<string name="app_title">SVXHomyak</string>
|
<string name="app_title">SVXHomyak</string>
|
||||||
<string name="btn_power">Выключить: отключиться и закрыть</string>
|
<string name="btn_power">Выключить: отключиться и закрыть</string>
|
||||||
|
<string name="btn_monitor">Мониторинг групп (нажмите, чтобы включить)</string>
|
||||||
|
<string name="btn_monitor_on">Мониторинг групп включен (нажмите, чтобы выключить)</string>
|
||||||
|
<string name="status_monitoring">Мониторинг: %1$s</string>
|
||||||
|
<string name="talker_mon">Говорит: %1$s · TG%2$d</string>
|
||||||
<string name="status_big_disconnected">DISCONNECTED</string>
|
<string name="status_big_disconnected">DISCONNECTED</string>
|
||||||
<string name="status_big_connected">CONNECTED</string>
|
<string name="status_big_connected">CONNECTED</string>
|
||||||
<string name="status_big_talking">ПЕРЕДАЧА</string>
|
<string name="status_big_talking">ПЕРЕДАЧА</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user