android radio: track independent manual PTT owners
This commit is contained in:
1 parent
163621ddfa
commit
1805b3dd27
3 files changed
+32
-7
No files matched your search
@@ -72,6 +72,7 @@ class RadioAudioEngine {
|
||||
@Volatile private var captureRunning = false
|
||||
@Volatile private var playbackRunning = false
|
||||
@Volatile private var requestedTalk = false
|
||||
private val pttHolds = RadioPttHolds()
|
||||
@Volatile private var talkRequestNs = 0L
|
||||
@Volatile private var generation = 0L
|
||||
private var audioGroup = 0L
|
||||
@@ -135,6 +136,7 @@ class RadioAudioEngine {
|
||||
active = true
|
||||
groupId = group
|
||||
requestedTalk = false
|
||||
pttHolds.cancel()
|
||||
_talkingFlow.value = false
|
||||
io.execute {
|
||||
if (!active || generation != version) return@execute
|
||||
@@ -156,6 +158,7 @@ class RadioAudioEngine {
|
||||
mainHandler.removeCallbacks(releaseTalk)
|
||||
active = false
|
||||
requestedTalk = false
|
||||
pttHolds.cancel()
|
||||
_talkingFlow.value = false
|
||||
++generation
|
||||
io.execute { releaseLocked() }
|
||||
@@ -513,7 +516,7 @@ class RadioAudioEngine {
|
||||
restoreAudioMode()
|
||||
}
|
||||
|
||||
fun talkBegin(): Boolean {
|
||||
internal fun talkBegin(source: RadioPttSource = RadioPttSource.BUTTON): Boolean {
|
||||
if (!active) return false
|
||||
if (!MicrophonePermission.granted()) {
|
||||
MicrophonePermission.request("PTT") {
|
||||
@@ -524,6 +527,8 @@ class RadioAudioEngine {
|
||||
return false
|
||||
}
|
||||
mainHandler.removeCallbacks(releaseTalk)
|
||||
pttHolds.press(source)
|
||||
LogManager.addLog("DEBUG", "RadioAudio", "PTT hold source=$source transmitting=$requestedTalk group=$groupId")
|
||||
if (requestedTalk) return true
|
||||
talkRequestNs = SystemClock.elapsedRealtimeNanos()
|
||||
requestedTalk = true
|
||||
@@ -540,15 +545,17 @@ class RadioAudioEngine {
|
||||
}
|
||||
|
||||
/** Отпускание: 250мс передачи, повторное касание отменяет завершение. */
|
||||
fun talkEnd() {
|
||||
internal fun talkEnd(source: RadioPttSource = RadioPttSource.BUTTON) {
|
||||
if (!pttHolds.release(source)) return
|
||||
if (!active || !requestedTalk) return
|
||||
mainHandler.removeCallbacks(releaseTalk)
|
||||
mainHandler.postDelayed(releaseTalk, RELEASE_HOLD_MS)
|
||||
LogManager.addLog("DEBUG", "RadioAudio", "PTT release scheduled +${RELEASE_HOLD_MS}ms")
|
||||
LogManager.addLog("DEBUG", "RadioAudio", "PTT release source=$source scheduled +${RELEASE_HOLD_MS}ms")
|
||||
}
|
||||
|
||||
/** Отмена без хвоста передачи при выключении/потере разрешений. */
|
||||
fun cancelTalk() {
|
||||
internal fun cancelTalk(source: RadioPttSource? = null) {
|
||||
if (!pttHolds.cancel(source)) return
|
||||
mainHandler.removeCallbacks(releaseTalk)
|
||||
finishTalk()
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ class RadioOverlayService : Service() {
|
||||
private fun removeOverlay() {
|
||||
val view = overlayView ?: return
|
||||
talking = false
|
||||
RadioAudioEngine.instance().cancelTalk()
|
||||
RadioAudioEngine.instance().cancelTalk(RadioPttSource.OVERLAY)
|
||||
try { windowManager?.removeView(view) } catch (e: Exception) {
|
||||
LogManager.addLog("WARN", "RadioOverlay", "Overlay removal failed: $e")
|
||||
}
|
||||
@@ -453,7 +453,7 @@ class RadioOverlayService : Service() {
|
||||
|
||||
private fun beginTalk() {
|
||||
if (talking) return
|
||||
if (!RadioAudioEngine.instance().talkBegin()) return
|
||||
if (!RadioAudioEngine.instance().talkBegin(RadioPttSource.OVERLAY)) return
|
||||
talking = true
|
||||
updateButtonColor()
|
||||
}
|
||||
@@ -461,7 +461,7 @@ class RadioOverlayService : Service() {
|
||||
private fun endTalk() {
|
||||
if (!talking) return
|
||||
talking = false
|
||||
RadioAudioEngine.instance().talkEnd()
|
||||
RadioAudioEngine.instance().talkEnd(RadioPttSource.OVERLAY)
|
||||
updateButtonColor()
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.utun.chat.data
|
||||
|
||||
/** Независимые владельцы ручной передачи. Все операции выполняются в главном потоке. */
|
||||
internal enum class RadioPttSource { BUTTON, OVERLAY, VOLUME }
|
||||
|
||||
internal class RadioPttHolds {
|
||||
private val held = mutableSetOf<RadioPttSource>()
|
||||
|
||||
fun press(source: RadioPttSource) { held.add(source) }
|
||||
|
||||
/** Завершать передачу можно только после отпускания последнего владельца. */
|
||||
fun release(source: RadioPttSource): Boolean = held.remove(source) && held.isEmpty()
|
||||
|
||||
fun cancel(source: RadioPttSource? = null): Boolean {
|
||||
if (source == null) held.clear() else held.remove(source)
|
||||
return held.isEmpty()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user