diff --git a/app/src/main/java/ru/ua1zbe/svxremote/MainActivity.kt b/app/src/main/java/ru/ua1zbe/svxremote/MainActivity.kt index 4c415f1..05795f4 100644 --- a/app/src/main/java/ru/ua1zbe/svxremote/MainActivity.kt +++ b/app/src/main/java/ru/ua1zbe/svxremote/MainActivity.kt @@ -9,6 +9,8 @@ import android.content.Intent import android.content.ServiceConnection import android.content.pm.PackageManager import android.net.Uri +import android.content.res.ColorStateList +import android.graphics.Color import android.graphics.PixelFormat import android.os.Build import android.os.Bundle @@ -57,10 +59,12 @@ class MainActivity : Activity(), SvxService.Listener { private lateinit var micDb: TextView private lateinit var pttButton: PttButton private lateinit var btnSettings: ImageButton + private lateinit var btnMonitor: ImageButton private val mainHandler = Handler(Looper.getMainLooper()) private var connected = false + private var monitoring = false private var settings: ConnectionSettings = ConnectionSettings("", 5300, "", "", 11) private var svc: SvxService? = null @@ -101,8 +105,11 @@ class MainActivity : Activity(), SvxService.Listener { micDb = findViewById(R.id.mic_db) pttButton = findViewById(R.id.ptt_button) btnSettings = findViewById(R.id.btn_settings) + btnMonitor = findViewById(R.id.btn_monitor) pttButton.enabled2 = false + btnMonitor.setOnClickListener { toggleMonitoring() } + updateMonitorButton() val testText = intent?.getStringExtra("node_toast_test") if (testText != null) { mainHandler.postDelayed({ showNodeOverlay(testText) }, 1500) @@ -239,7 +246,11 @@ class MainActivity : Activity(), SvxService.Listener { ReflectorClient.STATE_READY -> { connected = true 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)) lblTxState.text = getString(R.string.status_big_connected) lblTxState.setTextColor(getColor(R.color.accent)) @@ -280,7 +291,11 @@ class MainActivity : Activity(), SvxService.Listener { override fun onTalkerStart(tg: Long, callsign: String) { 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 } } @@ -366,7 +381,7 @@ class MainActivity : Activity(), SvxService.Listener { } private fun updatePttEnabled() { - pttButton.enabled2 = connected + pttButton.enabled2 = connected && !monitoring } private fun requestPermissionsIfNeeded() { @@ -391,12 +406,36 @@ class MainActivity : Activity(), SvxService.Listener { port = settings.port, callsign = settings.callsign, authKey = settings.authKey, - talkGroup = settings.talkGroup + talkGroup = settings.talkGroup, + monitorTgs = if (monitoring) settings.groups else null ) ) }, 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) { settings = settings.copy(talkGroup = tg) @@ -406,6 +445,9 @@ class MainActivity : Activity(), SvxService.Listener { mainHandler.postDelayed({ connect() }, 500) } + private fun monitorTgLabel(): String = + settings.groups.joinToString(", ") { "TG$it" } + private fun showNodesDialog() { val nodes = svc?.nodeList().orEmpty() if (nodes.isEmpty()) { diff --git a/app/src/main/java/ru/ua1zbe/svxremote/proto/ReflectorClient.kt b/app/src/main/java/ru/ua1zbe/svxremote/proto/ReflectorClient.kt index 65f339b..f74c9cd 100644 --- a/app/src/main/java/ru/ua1zbe/svxremote/proto/ReflectorClient.kt +++ b/app/src/main/java/ru/ua1zbe/svxremote/proto/ReflectorClient.kt @@ -58,7 +58,9 @@ class ReflectorClient( val port: Int, val callsign: String, val authKey: String, - val talkGroup: Long + val talkGroup: Long, + /** Список групп для мониторинга; null = обычный режим (своя группа). */ + val monitorTgs: List? = null ) @Volatile @@ -361,8 +363,14 @@ class ReflectorClient( // (иначе сервер не шлёт аудио группы и не показывает // подписку в статусе: monitoredTGs остаётся пустым). sendTcp(socket, MsgBuilder.nodeInfo(CLIENT_SW_INFO, CLIENT_SW_VERSION)) - sendTcp(socket, MsgBuilder.selectTg(s.talkGroup)) - sendTcp(socket, MsgBuilder.tgMonitor(listOf(s.talkGroup))) + val monitor = s.monitorTgs + if (monitor.isNullOrEmpty()) { + sendTcp(socket, MsgBuilder.selectTg(s.talkGroup)) + sendTcp(socket, MsgBuilder.tgMonitor(listOf(s.talkGroup))) + } else { + sendTcp(socket, MsgBuilder.selectTg(0)) + sendTcp(socket, MsgBuilder.tgMonitor(monitor)) + } openUdp(s) } diff --git a/app/src/main/res/drawable/bg_monitor_on.xml b/app/src/main/res/drawable/bg_monitor_on.xml new file mode 100644 index 0000000..e4b5c1b --- /dev/null +++ b/app/src/main/res/drawable/bg_monitor_on.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_headset.xml b/app/src/main/res/drawable/ic_headset.xml new file mode 100644 index 0000000..8eca7e0 --- /dev/null +++ b/app/src/main/res/drawable/ic_headset.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 3a8630d..6596292 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -84,6 +84,15 @@ android:ellipsize="end" android:text="@string/settings_not_configured" /> + + Отключено SVXHomyak Выключить: отключиться и закрыть + Мониторинг групп (нажмите, чтобы включить) + Мониторинг групп включен (нажмите, чтобы выключить) + Мониторинг: %1$s + Говорит: %1$s · TG%2$d DISCONNECTED CONNECTED ПЕРЕДАЧА