From 535c2cacf58ad094a278fa6e0f496cdf3eda01ff Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Sun, 5 Apr 2026 09:50:54 +0300 Subject: [PATCH] Completed multiple simulataneous calls feature --- .../com/tutpro/baresip/BaresipService.kt | 33 +++++++++---------- .../main/kotlin/com/tutpro/baresip/Call.kt | 12 ++----- .../com/tutpro/baresip/ConnectionService.kt | 2 -- 3 files changed, 17 insertions(+), 30 deletions(-) diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index 203662fc..fa090a9b 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -889,27 +889,19 @@ class BaresipService: Service() { val newHeldState = when (ev[1].toInt()) {Api.SDP_INACTIVE, Api.SDP_RECVONLY -> true else -> false } - val connection = ConnectionService.connections[callp] - - // Handle Remote Un-hold if (call!!.held && !newHeldState) { Log.d(TAG, "Call ${call.callp} un-held by peer.") - - // Clear our local manual hold flag so resume() can execute call.onhold = false - - // We use a Coroutine with a small delay to let the SIP + // Use a Coroutine with a small delay to let the SIP // transaction (the re-INVITE from the peer) finish - // before we try to hold the other call and resume this one. + // before trying to hold the other call and resume this one. CoroutineScope(Dispatchers.Main).launch { delay(100) call.resume() } } - call.held = newHeldState - if (newHeldState) { // Peer put us on hold call.showOnHoldNotice.value = true @@ -918,15 +910,12 @@ class BaresipService: Service() { } else { // Peer un-held us call.showOnHoldNotice.value = false - // Only clear the UI if we aren't also manually holding it - // (If we just called call.resume() above, it will handle this) if (!call.onhold) { call.callOnHold.value = false connection?.setActive() } } - if (call.state() == Api.CALL_STATE_EARLY) { if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0) stopMediaPlayer() @@ -935,7 +924,6 @@ class BaresipService: Service() { playRingBack() } } - if (call.status.value == "connected" && !call.held && !call.onhold) { if (call.callOnHold.value || call.showOnHoldNotice.value) { Log.d(TAG, "Safety guard: Clearing stuck hold flags for ${call.callp}") @@ -1006,9 +994,17 @@ class BaresipService: Service() { } "call closed" -> { Log.d(TAG, "AoR $aor call $callp is closed prm: ${ev[1]}") - ConnectionService.connections[callp]?.let { - it.setDisconnected(DisconnectCause(DisconnectCause.REMOTE)) - it.destroy() + val connection = ConnectionService.connections[callp] + if (connection != null) { + val cause = when { + ev[1].contains("200") -> DisconnectCause.LOCAL + ev[1].contains("486") -> DisconnectCause.BUSY + ev[1].contains("404") -> DisconnectCause.ERROR + ev[1].contains("403") || ev[1].contains("401") -> DisconnectCause.RESTRICTED + else -> DisconnectCause.REMOTE + } + connection.setDisconnected(DisconnectCause(cause)) + connection.destroy() ConnectionService.connections.remove(callp) } nm.cancel(CALL_NOTIFICATION_ID) @@ -1032,9 +1028,10 @@ class BaresipService: Service() { onHoldCall.referTo = "" call.onHoldCall = null } + val isConference = call.conferenceCall call.remove() updateStatusNotification() - if (call.conferenceCall && ua.calls().isEmpty()) + if (isConference && ua.calls().isEmpty()) Api.module_unload("mixminus") val reason = ev[1] val tone = ev[2] diff --git a/app/src/main/kotlin/com/tutpro/baresip/Call.kt b/app/src/main/kotlin/com/tutpro/baresip/Call.kt index cc1ab3d3..0ba1f01b 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Call.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Call.kt @@ -62,8 +62,6 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str if (Api.call_hold(callp, true)) { onhold = true callOnHold.value = true - // Fix: Do NOT set showOnHoldNotice to true here. - // That notice is for when the PEER holds us. showOnHoldNotice.value = false ConnectionService.connections[callp]?.setOnHold() return true @@ -73,7 +71,6 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str fun resume(): Boolean { if (!onhold && !held) return true - // 1. Hold other calls first for (c in BaresipService.calls) { if (c.callp != this.callp && !c.onhold && !c.held) { @@ -81,15 +78,12 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str c.hold() } } - val connection = ConnectionService.connections[callp] - // 2. SIP Signaling if (Api.call_hold(callp, false)) { onhold = false callOnHold.value = false showOnHoldNotice.value = false - // 3. Telecom Sync connection?.setAddress("sip:$peerUri".toUri(), android.telecom.TelecomManager.PRESENTATION_ALLOWED) connection?.setActive() @@ -99,10 +93,8 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str } fun transfer(uri: String): Boolean { - if (!Api.call_hold(callp, true)) - return false - onhold = true - referTo = uri + if (!onhold) hold() + Log.d(TAG, "Transferring call $callp to $uri") return Api.call_transfer(callp, uri) == 0 } diff --git a/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt b/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt index 686bd8a2..a59ae824 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ConnectionService.kt @@ -201,8 +201,6 @@ class ConnectionService : ConnectionService() { } override fun onHold() { - // Do NOT call super.onHold() first, as it might change internal state - // before we can grab the call object. Log.d(TAG, "Telecom Connection onHold $callp") val call = Call.ofCallp(callp) if (call != null && !call.conferenceCall) {