Browse Source

android radio: release volume PTT on focus loss and option disable

master
evgeny 17 hours ago
parent
commit
40e6bd36c3
  1. 45
      tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt
  2. 15
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/ConfigProvider.kt

45
tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt

@ -39,6 +39,7 @@ import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.compose.LifecycleStartEffect
import androidx.lifecycle.lifecycleScope
import com.utun.chat.data.Channel
import com.utun.chat.data.IncomingCallManager
import com.utun.chat.data.InviteData
@ -48,12 +49,14 @@ import com.utun.chat.data.ConversationTarget
import com.utun.chat.data.NativeLib
import com.utun.chat.data.RadioOverlayService
import com.utun.chat.data.RadioAudioEngine
import com.utun.chat.data.RadioPttSource
import com.utun.chat.headless.HeadlessService
import com.utun.chat.ui.theme.VibeChatTheme
import com.utun.chat.ui.components.ActiveCallBanner
import com.utun.chat.ui.screens.*
import com.utun.chat.viewmodel.ChatViewModel
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.collect
class MainActivity : AppCompatActivity() {
enum class Screen { ChannelList, Chat, MemberList, Settings, Logs, VideoRecord, PhotoRecord, Call, Dm }
@ -129,6 +132,11 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
ChatApplication.instance.configProvider.radioVolumePttEnabled.collect { enabled ->
if (!enabled) releaseVolumePtt("option-disabled")
}
}
LogManager.addLog("INFO", "LANGUAGE",
"UI language applied: selected=${AppLanguage.selectedTag().ifEmpty { "system" }} " +
@ -479,6 +487,7 @@ class MainActivity : AppCompatActivity() {
}
override fun onPause() {
releaseVolumePtt("activity-paused")
RadioOverlayService.setActivityVisible(false)
super.onPause()
}
@ -544,31 +553,39 @@ class MainActivity : AppCompatActivity() {
}
/** Volume-кнопки (удержание) → PTT рации, при активной рации и включённой опции. */
private var pttHoldCount = 0
private val pttVolumeKeys = mutableSetOf<Int>()
private fun releaseVolumePtt(reason: String) {
if (pttVolumeKeys.isEmpty()) return
pttVolumeKeys.clear()
RadioAudioEngine.instance().cancelTalk(RadioPttSource.VOLUME)
LogManager.addLog("INFO", "PTT", "Volume hold cancelled: $reason")
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
if (!hasFocus) releaseVolumePtt("window-focus-lost")
super.onWindowFocusChanged(hasFocus)
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
val isVol = event.keyCode == KeyEvent.KEYCODE_VOLUME_UP || event.keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
val pttOn = ChatApplication.instance.configProvider.getRadioVolumePtt()
&& RadioAudioEngine.instance().isActive()
if (!pttOn || !isVol) {
pttHoldCount = 0
return super.dispatchKeyEvent(event)
}
if (!pttOn) releaseVolumePtt("disabled-or-radio-inactive")
if (!pttOn || !isVol) return super.dispatchKeyEvent(event)
when (event.action) {
KeyEvent.ACTION_DOWN -> {
if (event.repeatCount == 0) {
if (pttHoldCount == 0 && !RadioAudioEngine.instance().talkBegin()) return true
pttHoldCount++
LogManager.addLog("DEBUG", "PTT", "key=${event.keyCode} down hold=$pttHoldCount")
if (event.repeatCount == 0 && event.keyCode !in pttVolumeKeys) {
if (pttVolumeKeys.isEmpty() && !RadioAudioEngine.instance().talkBegin(RadioPttSource.VOLUME)) return true
pttVolumeKeys.add(event.keyCode)
LogManager.addLog("DEBUG", "PTT", "key=${event.keyCode} down held=$pttVolumeKeys")
}
return true
}
KeyEvent.ACTION_UP -> {
if (pttHoldCount > 0) {
pttHoldCount--
if (pttHoldCount == 0) RadioAudioEngine.instance().talkEnd()
}
LogManager.addLog("DEBUG", "PTT", "key=${event.keyCode} up hold=$pttHoldCount")
if (pttVolumeKeys.remove(event.keyCode) && pttVolumeKeys.isEmpty())
RadioAudioEngine.instance().talkEnd(RadioPttSource.VOLUME)
LogManager.addLog("DEBUG", "PTT", "key=${event.keyCode} up held=$pttVolumeKeys cancelled=${event.isCanceled}")
return true
}
}

15
tools/chatgui-android/app/src/main/java/com/utun/chat/data/ConfigProvider.kt

@ -17,6 +17,7 @@ import java.net.Inet6Address
import java.net.InetAddress
import java.net.NetworkInterface
import java.io.File
import java.io.IOException
import java.util.UUID
import java.util.concurrent.ThreadLocalRandom
@ -330,7 +331,7 @@ class ConfigProvider(private val context: Context) {
/* ── Рация: volume-кнопки PTT и оверлей ── */
/* Кэш быстрых настроек рации: чтение не блокирует главный поток.
* Ленивая загрузка один раз, дальше — из памяти; set обновляет кэш сразу. */
* Ленивая загрузка один раз, дальше — из памяти; set обновляет кэш после сохранения. */
@Volatile private var radioVolumePttCached = false
@Volatile private var radioVolumePttLoaded = false
@ -343,11 +344,17 @@ class ConfigProvider(private val context: Context) {
}
suspend fun setRadioVolumePtt(enabled: Boolean) {
radioVolumePttCached = enabled
radioVolumePttLoaded = true
context.dataStore.edit { prefs -> prefs[radioVolumePttKey] = enabled }
try {
context.dataStore.edit { prefs -> prefs[radioVolumePttKey] = enabled }
radioVolumePttCached = enabled
radioVolumePttLoaded = true
LogManager.addLog("INFO", "PTT", "Volume PTT enabled=$enabled")
} catch (e: IOException) {
LogManager.addLog("ERROR", "PTT", "Cannot save volume PTT enabled=$enabled: $e")
}
}
val radioVolumePttEnabled: Flow<Boolean> = context.dataStore.data.map { it[radioVolumePttKey] ?: false }
val radioOverlayEnabled: Flow<Boolean> = context.dataStore.data.map { it[radioOverlayKey] ?: false }
val radioVadEnabled: Flow<Boolean> = context.dataStore.data.map { (it[radioVadEnabledKey] ?: 0) != 0 }

Loading…
Cancel
Save