Contain call notification and route persistence failures

This commit is contained in:
evgeny committed 2026-10-29 13:34:25 +01:00
1 parent 7e328b3908
commit 236dbd3e84
3 files changed
+23 -7

No files matched your search

@@ -32,6 +32,8 @@ data class OutgoingCallRequest(val channelId: String, val peerNodeId: Long, val
* Единственный владелец звонковой сессии в процессе приложения. Все операции — main thread.
* Сервис владеет аудио и wake lock, ядро — сигналингом, ViewModel только читает StateFlow.
* События поступают напрямую из AppEventHandler без replay старых звонков новым подписчикам.
* Start/accept ждут освобождения чужих media и успешного enqueue; hangup/decline при отказе очереди
* повторяются независимо от UI/audio cleanup. Core reset отменяет такие повторы до остановки worker.
*/
object CallController : TelecomCallManager.Listener {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
@@ -229,8 +231,9 @@ object CallController : TelecomCallManager.Listener {
throw e
} catch (e: Exception) {
LogManager.addLog("ERROR", "Call", "media release failed id=$id: $e")
Toast.makeText(ChatApplication.instance, R.string.call_media_stop_failed, Toast.LENGTH_LONG).show()
end(id, 7)
try { Toast.makeText(ChatApplication.instance, R.string.call_media_stop_failed, Toast.LENGTH_LONG).show() }
catch (notify: Exception) { LogManager.addLog("ERROR", "Call", "Cannot show media release failure id=$id: $notify") }
}
}
}
@@ -382,17 +385,17 @@ object CallController : TelecomCallManager.Listener {
radioToRestore = null
RadioAudioEngine.instance().restoreAfterCall(group)
}
if (reason == 8 && st.phase != CallPhase.INCOMING && st.phase != CallPhase.PENDING_INCOMING) {
if (reason == 8 && st.phase != CallPhase.INCOMING && st.phase != CallPhase.PENDING_INCOMING) try {
val app = ChatApplication.instance
val text = AppLanguage.text(R.string.phone_busy_warning)
if (activityVisible) Toast.makeText(app, text, Toast.LENGTH_LONG).show()
else try {
else {
requireNotNull(app.getSystemService(NotificationManager::class.java)) { "notification manager unavailable" }.notify(1003,
NotificationCompat.Builder(app, IncomingCallManager.CHANNEL_CALL)
.setSmallIcon(android.R.drawable.ic_dialog_info).setContentTitle(st.peerName)
.setContentText(text).setAutoCancel(true).build())
} catch (e: Exception) { LogManager.addLog("ERROR", "Call", "Busy outcome notification failed id=$id: $e") }
}
}
} catch (e: Exception) { LogManager.addLog("ERROR", "Call", "Busy outcome notification failed id=$id: $e") }
scope.launch {
delay(2500)
if (_state.value?.callId == id && _state.value?.phase == CallPhase.ENDED) _state.value = null
@@ -429,10 +432,21 @@ object CallController : TelecomCallManager.Listener {
fun setRoute(route: AudioRoute) {
checkMain()
if (_state.value?.phase != CallPhase.ACTIVE || service == null) {
LogManager.addLog("WARN", "Call", "route ignored outside active call route=$route phase=${_state.value?.phase}")
return
}
videoSpeakerPending = false // Явный выбор пользователя отменяет ожидающее автоматическое переключение.
service?.audio?.router?.setRoute(route)
syncProximity()
scope.launch { ChatApplication.instance.configProvider.setCallAudioRoute(route) }
scope.launch {
try { ChatApplication.instance.configProvider.setCallAudioRoute(route) }
catch (e: CancellationException) { throw e }
catch (e: Exception) {
LogManager.addLog("ERROR", "Call", "Cannot save preferred route=$route: $e")
Toast.makeText(ChatApplication.instance, R.string.call_route_save_failed, Toast.LENGTH_LONG).show()
}
}
}
private fun routeChanged(route: AudioRoute, headset: Boolean) {
@@ -466,7 +480,7 @@ object CallController : TelecomCallManager.Listener {
audio.router.fallbackRoute = AudioRoute.SPEAKER
audio.router.setRoute(AudioRoute.SPEAKER, automatic = true)
syncProximity()
LogManager.addLog("INFO", "call", "video speaker enabled after confirmed FAR id=${st.callId}")
LogManager.addLog("INFO", "call", "video speaker requested after confirmed FAR id=${st.callId}")
}
/** Только живые события ядра, сериализованные AppEventHandler на main thread. */
@@ -33,6 +33,7 @@
<string name="call_incoming_stop_media">При принятии звонка рация выключится, трансляция или её просмотр завершится. После звонка рация включится снова.</string>
<string name="call_preparing_audio">Освобождаем аудиоустройства…</string>
<string name="call_media_stop_failed">Не удалось освободить аудиоустройства для звонка.</string>
<string name="call_route_save_failed">Не удалось сохранить предпочтительное аудиоустройство звонков.</string>
<string name="call_media_busy">Во время звонка звук рации недоступен.</string>
<string name="call_video_settings">Настройки видео</string>
<string name="call_video_unavailable">Не удалось включить передачу видео</string>
@@ -33,6 +33,7 @@
<string name="call_incoming_stop_media">Answering will turn off the radio and end the broadcast or stream playback. The radio will turn back on after the call.</string>
<string name="call_preparing_audio">Releasing audio devices…</string>
<string name="call_media_stop_failed">Could not release the audio devices for the call.</string>
<string name="call_route_save_failed">Could not save the preferred call audio device.</string>
<string name="call_media_busy">Radio audio is unavailable during a call.</string>
<string name="call_video_settings">Video settings</string>
<string name="call_video_unavailable">Unable to send video</string>