Honor Telecom incoming UI permission and report phone busy outcomes
This commit is contained in:
1 parent
4cebd0be54
commit
bd2dd056d6
3 files changed
+72
-6
No files matched your search
@@ -1,6 +1,10 @@
|
||||
package com.utun.chat.data
|
||||
|
||||
import android.content.Intent
|
||||
import android.app.NotificationManager
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.utun.chat.R
|
||||
import android.os.Looper
|
||||
import android.os.SystemClock
|
||||
import com.utun.chat.ChatApplication
|
||||
@@ -76,7 +80,7 @@ object CallController : TelecomCallManager.Listener {
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
if (st.phase == CallPhase.ACTIVE) {
|
||||
_state.value = st.copy(durationSeconds = ((now - st.startedAtElapsedRealtime) / 1000).toInt())
|
||||
} else if (st.phase == CallPhase.INCOMING && now - incomingAt > 50_000) {
|
||||
} else if (st.phase == CallPhase.INCOMING && now - incomingAt > 60_000) {
|
||||
LogManager.addLog("WARN", "Call", "incoming timeout id=${st.callId}; declining")
|
||||
end(st.callId)
|
||||
}
|
||||
@@ -238,6 +242,17 @@ object CallController : TelecomCallManager.Listener {
|
||||
connectionType = CallConnectionType.NONE, connectionNextHop = 0)
|
||||
syncCoreActivity()
|
||||
LogManager.addLog("INFO", "Call", "finished id=$id reason=$reason; audio/service/proximity released")
|
||||
if (reason == 8 && st.phase != CallPhase.INCOMING && st.phase != CallPhase.PENDING_INCOMING) {
|
||||
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 {
|
||||
app.getSystemService(NotificationManager::class.java).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: SecurityException) { LogManager.addLog("WARN", "Call", "Busy outcome notification denied id=$id: $e") }
|
||||
}
|
||||
scope.launch {
|
||||
delay(2500)
|
||||
if (_state.value?.callId == id && _state.value?.phase == CallPhase.ENDED) _state.value = null
|
||||
@@ -320,7 +335,7 @@ object CallController : TelecomCallManager.Listener {
|
||||
return
|
||||
}
|
||||
if (type == 13) { reset(); return }
|
||||
if (type !in 34..40 && type != 42 && type != 44) return
|
||||
if (type !in 34..40 && type != 42 && type != 44 && type != 66) return
|
||||
if (!coreReady || !NativeLib.isReady()) {
|
||||
LogManager.addLog("WARN", "Call", "ignore event=$type while core stopped/restarting")
|
||||
return
|
||||
@@ -345,10 +360,12 @@ object CallController : TelecomCallManager.Listener {
|
||||
// Однократное чтение на main завершается до reset; фоновый JNI-запрос не переживает ядро.
|
||||
val name = ChatRepository().getMembers(channel).firstOrNull { it.nodeId == peer }?.name
|
||||
?.takeIf { it.isNotEmpty() } ?: "0x%016x".format(peer)
|
||||
_state.value = CallState(id, channel, peer, name, CallPhase.INCOMING)
|
||||
_state.value = CallState(id, channel, peer, name,
|
||||
if (TelecomCallManager.isReady()) CallPhase.PENDING_INCOMING else CallPhase.INCOMING)
|
||||
syncCoreActivity()
|
||||
IncomingCallManager.show(id, channel, peer)
|
||||
TelecomCallManager.addIncomingCall(id, peer, name)
|
||||
if (TelecomCallManager.isReady()) {
|
||||
if (!TelecomCallManager.addIncomingCall(id, peer, name)) end(id, 7)
|
||||
} else IncomingCallManager.show(id, channel, peer)
|
||||
return
|
||||
}
|
||||
if (data == null || data.size < 8 || ((type == 37 || type == 38 || type == 40 || type == 42) && data.size < 9) ||
|
||||
@@ -361,6 +378,12 @@ object CallController : TelecomCallManager.Listener {
|
||||
return
|
||||
}
|
||||
when (type) {
|
||||
66 -> if (st.phase == CallPhase.OUTGOING) {
|
||||
CallRingback.stop()
|
||||
_state.value = st.copy(phase = CallPhase.WAIT_DECISION)
|
||||
service?.updateNotification()
|
||||
LogManager.addLog("INFO", "Call", "Phone busy; waiting for caller choice id=$id")
|
||||
}
|
||||
35 -> if (st.phase == CallPhase.OUTGOING) {
|
||||
_state.value = st.copy(phase = CallPhase.RINGING)
|
||||
CallRingback.startRinging()
|
||||
@@ -427,6 +450,9 @@ object CallController : TelecomCallManager.Listener {
|
||||
}
|
||||
|
||||
override fun onAnswer(callId: Long) {
|
||||
val pending = _state.value
|
||||
if (pending?.callId == callId && pending.phase == CallPhase.PENDING_INCOMING)
|
||||
_state.value = pending.copy(phase = CallPhase.INCOMING)
|
||||
if (_state.value?.callId != callId || _state.value?.phase != CallPhase.INCOMING) return
|
||||
if (activityVisible) { accept(); return }
|
||||
// Ответ гарнитурой в фоне: сначала видимый UI, затем разрешённый запуск microphone FGS.
|
||||
@@ -445,5 +471,29 @@ object CallController : TelecomCallManager.Listener {
|
||||
override fun onReject(callId: Long) { end(callId, 2) }
|
||||
override fun onDisconnect(callId: Long) { end(callId) }
|
||||
|
||||
override fun onIncomingUiAllowed(callId: Long) {
|
||||
val st = _state.value ?: return
|
||||
if (st.callId != callId || st.phase != CallPhase.PENDING_INCOMING) return
|
||||
_state.value = st.copy(phase = CallPhase.INCOMING)
|
||||
LogManager.addLog("INFO", "Call", "Telecom permits incoming UI id=$callId")
|
||||
IncomingCallManager.show(callId, st.channelId, st.peerNodeId)
|
||||
}
|
||||
|
||||
override fun onConnectionFailed(callId: Long) {
|
||||
LogManager.addLog("WARN", "Call", "Telecom cannot handle call id=$callId")
|
||||
end(callId, 7)
|
||||
}
|
||||
|
||||
fun continueCall(id: Long) {
|
||||
checkMain()
|
||||
val st = _state.value ?: return
|
||||
if (st.callId != id || st.phase != CallPhase.WAIT_DECISION) return
|
||||
LogManager.addLog("INFO", "Call", "Caller overrides phone busy id=$id")
|
||||
_state.value = st.copy(phase = CallPhase.OUTGOING)
|
||||
NativeLib.callContinue(id)
|
||||
CallRingback.startConnecting()
|
||||
service?.updateNotification()
|
||||
}
|
||||
|
||||
private fun checkMain() = check(Looper.myLooper() == Looper.getMainLooper()) { "CallController requires main thread" }
|
||||
}
|
||||
@@ -30,6 +30,8 @@ object TelecomCallManager {
|
||||
fun onReject(callId: Long)
|
||||
fun onDisconnect(callId: Long)
|
||||
fun onConnectionAvailable(callId: Long, connection: UtunConnection)
|
||||
fun onIncomingUiAllowed(callId: Long)
|
||||
fun onConnectionFailed(callId: Long)
|
||||
}
|
||||
|
||||
const val EXTRA_CALL_ID = "com.utun.chat.extra.CALL_ID"
|
||||
@@ -134,7 +136,7 @@ object TelecomCallManager {
|
||||
connections[callId] = conn
|
||||
when (st.phase) {
|
||||
CallPhase.ACTIVE -> conn.markActive()
|
||||
CallPhase.INCOMING -> conn.markRinging()
|
||||
CallPhase.INCOMING, CallPhase.PENDING_INCOMING -> conn.markRinging()
|
||||
else -> conn.setDialing()
|
||||
}
|
||||
LogManager.addLog("INFO", TAG, "connection registered id=$callId phase=${st.phase}")
|
||||
@@ -152,6 +154,8 @@ object TelecomCallManager {
|
||||
fun notifyDisconnect(callId: Long) = post { listener?.onDisconnect(callId) }
|
||||
|
||||
fun notifyAbort(callId: Long) = post { listener?.onDisconnect(callId) }
|
||||
fun notifyIncomingUiAllowed(callId: Long) = post { listener?.onIncomingUiAllowed(callId) }
|
||||
fun notifyConnectionFailed(callId: Long) = post { listener?.onConnectionFailed(callId) }
|
||||
|
||||
/* ── управление состоянием из контроллера ── */
|
||||
|
||||
|
||||
@@ -14,6 +14,18 @@ import android.telecom.PhoneAccountHandle
|
||||
*/
|
||||
class UtunConnectionService : ConnectionService() {
|
||||
|
||||
override fun onCreateIncomingConnectionFailed(account: PhoneAccountHandle?, request: ConnectionRequest?) {
|
||||
val id = request?.extras?.getLong(TelecomCallManager.EXTRA_CALL_ID, 0) ?: 0
|
||||
LogManager.addLog("WARN", "UtunConnSvc", "Telecom rejected incoming call id=$id")
|
||||
TelecomCallManager.notifyConnectionFailed(id)
|
||||
}
|
||||
|
||||
override fun onCreateOutgoingConnectionFailed(account: PhoneAccountHandle?, request: ConnectionRequest?) {
|
||||
val id = request?.extras?.getLong(TelecomCallManager.EXTRA_CALL_ID, 0) ?: 0
|
||||
LogManager.addLog("WARN", "UtunConnSvc", "Telecom rejected outgoing call id=$id")
|
||||
TelecomCallManager.notifyConnectionFailed(id)
|
||||
}
|
||||
|
||||
override fun onCreateIncomingConnection(
|
||||
connectionManagerPhoneAccount: PhoneAccountHandle?,
|
||||
request: ConnectionRequest?
|
||||
|
||||
Reference in new issue
Block a user