Allow switching between active call and call on-hold
This commit is contained in:
@@ -886,37 +886,46 @@ class BaresipService: Service() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
"call update" -> {
|
"call update" -> {
|
||||||
val newHeldState = when (ev[1].toInt()) {
|
val newHeldState = when (ev[1].toInt()) {Api.SDP_INACTIVE, Api.SDP_RECVONLY -> true
|
||||||
Api.SDP_INACTIVE, Api.SDP_RECVONLY -> true
|
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
val connection = ConnectionService.connections[callp]
|
val connection = ConnectionService.connections[callp]
|
||||||
|
|
||||||
|
// Handle Remote Un-hold
|
||||||
if (call!!.held && !newHeldState) {
|
if (call!!.held && !newHeldState) {
|
||||||
Log.d(TAG, "Call ${call.callp} un-held by peer. Requesting Telecom Active.")
|
Log.d(TAG, "Call ${call.callp} un-held by peer.")
|
||||||
// Clear local UI state
|
|
||||||
call.onhold = false
|
|
||||||
call.callOnHold.value = false
|
|
||||||
call.showOnHoldNotice.value = false
|
|
||||||
|
|
||||||
// Tell Android to make this call active.
|
// Clear our local manual hold flag so resume() can execute
|
||||||
// Telecom will automatically trigger onHold for other connections.
|
call.onhold = false
|
||||||
connection?.setActive()
|
|
||||||
|
// We 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.
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
delay(100)
|
||||||
|
call.resume()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
call.held = newHeldState
|
call.held = newHeldState
|
||||||
|
|
||||||
if (newHeldState) {
|
if (newHeldState) {
|
||||||
connection?.setOnHold()
|
// Peer put us on hold
|
||||||
call.callOnHold.value = true
|
|
||||||
call.showOnHoldNotice.value = true
|
call.showOnHoldNotice.value = true
|
||||||
} else if (!call.onhold) {
|
call.callOnHold.value = true
|
||||||
// Only set active if we haven't manually put it on hold ourselves
|
connection?.setOnHold()
|
||||||
call.callOnHold.value = false
|
} else {
|
||||||
|
// Peer un-held us
|
||||||
call.showOnHoldNotice.value = false
|
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()
|
connection?.setActive()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (call.state() == Api.CALL_STATE_EARLY) {
|
if (call.state() == Api.CALL_STATE_EARLY) {
|
||||||
if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0)
|
if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0)
|
||||||
@@ -926,6 +935,15 @@ class BaresipService: Service() {
|
|||||||
playRingBack()
|
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}")
|
||||||
|
call.callOnHold.value = false
|
||||||
|
call.showOnHoldNotice.value = false
|
||||||
|
connection?.setActive()
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!isMainVisible || call.status.value != "connected")
|
if (!isMainVisible || call.status.value != "connected")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,38 +59,43 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
|
|||||||
|
|
||||||
fun hold(): Boolean {
|
fun hold(): Boolean {
|
||||||
if (onhold) return true
|
if (onhold) return true
|
||||||
val connection = ConnectionService.connections[callp]
|
|
||||||
if (connection != null) {
|
|
||||||
connection.setOnHold()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// Fallback if no Telecom connection exists
|
|
||||||
if (Api.call_hold(callp, true)) {
|
if (Api.call_hold(callp, true)) {
|
||||||
onhold = true
|
onhold = true
|
||||||
callOnHold.value = true
|
callOnHold.value = true
|
||||||
showOnHoldNotice.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
|
||||||
}
|
}
|
||||||
return onhold
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resume(): Boolean {
|
fun resume(): Boolean {
|
||||||
if (!onhold) return true
|
if (!onhold && !held) return true
|
||||||
val connection = ConnectionService.connections[callp]
|
|
||||||
if (connection != null) {
|
// 1. Hold other calls first
|
||||||
connection.setAddress(
|
for (c in BaresipService.calls) {
|
||||||
"sip:$peerUri".toUri(),
|
if (c.callp != this.callp && !c.onhold && !c.held) {
|
||||||
android.telecom.TelecomManager.PRESENTATION_ALLOWED
|
Log.d("Baresip", "Auto-holding active call ${c.callp}")
|
||||||
)
|
c.hold()
|
||||||
connection.setActive()
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
// Fallback if no Telecom connection exists
|
}
|
||||||
|
|
||||||
|
val connection = ConnectionService.connections[callp]
|
||||||
|
|
||||||
|
// 2. SIP Signaling
|
||||||
if (Api.call_hold(callp, false)) {
|
if (Api.call_hold(callp, false)) {
|
||||||
onhold = false
|
onhold = false
|
||||||
callOnHold.value = false
|
callOnHold.value = false
|
||||||
showOnHoldNotice.value = false
|
showOnHoldNotice.value = false
|
||||||
|
|
||||||
|
// 3. Telecom Sync
|
||||||
|
connection?.setAddress("sip:$peerUri".toUri(), android.telecom.TelecomManager.PRESENTATION_ALLOWED)
|
||||||
|
connection?.setActive()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return !onhold
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun transfer(uri: String): Boolean {
|
fun transfer(uri: String): Boolean {
|
||||||
|
|||||||
@@ -201,26 +201,40 @@ class ConnectionService : ConnectionService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onHold() {
|
override fun onHold() {
|
||||||
super.onHold()
|
// Do NOT call super.onHold() first, as it might change internal state
|
||||||
Log.d(TAG, "Telecom requested Hold for $callp")
|
// before we can grab the call object.
|
||||||
val call = BaresipService.calls.find { it.callp == this.callp }
|
Log.d(TAG, "Telecom Connection onHold $callp")
|
||||||
if (call != null && !call.onhold && !call.conferenceCall) {
|
val call = Call.ofCallp(callp)
|
||||||
|
if (call != null && !call.conferenceCall) {
|
||||||
|
// 1. Force SIP Signaling
|
||||||
|
if (Api.call_hold(call.callp, true)) {
|
||||||
|
// 2. Sync Call object state
|
||||||
call.onhold = true
|
call.onhold = true
|
||||||
Api.call_hold(call.callp, true)
|
call.callOnHold.value = true
|
||||||
|
call.showOnHoldNotice.value = true
|
||||||
|
// 3. Tell Telecom the move is complete
|
||||||
|
setOnHold()
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "SIP Hold failed for $callp")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUnhold() {
|
override fun onUnhold() {
|
||||||
Log.d(TAG, "Telecom Connection onUnhold $callp")
|
Log.d(TAG, "Telecom Connection onUnhold $callp")
|
||||||
val call = BaresipService.calls.find { it.callp == this.callp }
|
val call = Call.ofCallp(callp)
|
||||||
if (call != null) {
|
if (call != null && !call.conferenceCall) {
|
||||||
if (!call.conferenceCall) {
|
// 1. Force SIP Signaling
|
||||||
|
if (Api.call_hold(call.callp, false)) {
|
||||||
|
// 2. Sync Call object state
|
||||||
call.onhold = false
|
call.onhold = false
|
||||||
call.callOnHold.value = false
|
call.callOnHold.value = false
|
||||||
call.showOnHoldNotice.value = false
|
call.showOnHoldNotice.value = false
|
||||||
Api.call_hold(call.callp, false)
|
// 3. Tell Telecom we are active
|
||||||
}
|
|
||||||
setActive()
|
setActive()
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "SIP Resume failed for $callp")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1538,33 +1538,14 @@ private fun CallRow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!call.conferenceCall)
|
if (!call.conferenceCall)
|
||||||
IconButton(
|
IconButton( modifier = Modifier.size(48.dp),
|
||||||
modifier = Modifier.size(48.dp),
|
|
||||||
onClick = {
|
onClick = {
|
||||||
val connection = ConnectionService.connections[call.callp]
|
|
||||||
if (call.callOnHold.value) {
|
if (call.callOnHold.value) {
|
||||||
if (!Call.isAnyCallActive(ctx)) {
|
Log.d(TAG, "User requested resume for ${call.callp}")
|
||||||
Log.d(
|
call.resume() // This now automatically holds other calls
|
||||||
TAG,
|
} else {
|
||||||
"AoR ${call.ua.account.aor} resuming call ${call.callp} with ${call.callUri.value}"
|
Log.d(TAG, "User requested hold for ${call.callp}")
|
||||||
)
|
|
||||||
if (connection != null)
|
|
||||||
connection.onUnhold()
|
|
||||||
else
|
|
||||||
call.resume()
|
|
||||||
call.callOnHold.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Log.d(
|
|
||||||
TAG,
|
|
||||||
"AoR ${call.ua.account.aor} holding call ${call.callp} with ${call.callUri.value}"
|
|
||||||
)
|
|
||||||
if (connection != null)
|
|
||||||
connection.onHold()
|
|
||||||
else
|
|
||||||
call.hold()
|
call.hold()
|
||||||
call.callOnHold.value = true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user