Started experimenting with baresip as PSTN dialer

This commit is contained in:
Juha Heinanen
2026-05-08 17:25:19 +03:00
parent 20d1312d0f
commit 0a04a3e132
8 changed files with 309 additions and 66 deletions
@@ -12,12 +12,14 @@ import android.content.res.Configuration
import android.media.AudioManager
import android.net.Uri
import android.os.Build.VERSION
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Process
import android.os.SystemClock
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.telecom.TelecomManager
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@@ -1473,12 +1475,14 @@ private fun CallRow(
horizontalArrangement = Arrangement.Absolute.SpaceBetween
) {
if (isDialer) {
dialerState.showCallPstnButton.value = Utils.pstnAccountHandle(ctx) != null
if (dialerState.showCallButton.value)
IconButton(
modifier = Modifier.size(48.dp),
enabled = dialerState.callButtonsEnabled.value,
onClick = {
if (!dialerState.callButtonsEnabled.value) return@IconButton
dialerState.showCallPstnButton.value = false
dialerState.showCallConferenceButton.value = false
dialerState.showSuggestions.value = false
callClick(ctx, viewModel, dialerState)
@@ -1494,6 +1498,28 @@ private fun CallRow(
contentDescription = null,
)
}
if (dialerState.showCallPstnButton.value)
IconButton(
modifier = Modifier.size(48.dp),
enabled = dialerState.callButtonsEnabled.value,
onClick = {
if (!dialerState.callButtonsEnabled.value) return@IconButton
dialerState.showCallButton.value = false
dialerState.showCallConferenceButton.value = false
dialerState.showSuggestions.value = false
callClick(ctx, viewModel, dialerState)
},
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.call_tel),
modifier = Modifier.size(42.dp),
tint = colorResource(if (dialerState.callButtonsEnabled.value)
R.color.colorTrafficGreen
else
R.color.colorTrafficYellow),
contentDescription = null,
)
}
if (dialerState.showCallConferenceButton.value) {
Spacer(modifier = Modifier.weight(1f, true))
IconButton(
@@ -1502,6 +1528,7 @@ private fun CallRow(
onClick = {
if (!dialerState.callButtonsEnabled.value) return@IconButton
dialerState.showCallButton.value = false
dialerState.showCallPstnButton.value = false
dialerState.showSuggestions.value = false
callClick(ctx, viewModel, dialerState)
}
@@ -2061,14 +2088,14 @@ private fun callClick(ctx: Context, viewModel: ViewModel, dialerState: ViewModel
ctx,
viewModel,
uriText,
dialerState.showCallConferenceButton.value
dialerState
)
else if (uris.size == 1)
makeCall(
ctx,
viewModel,
uris[0],
dialerState.showCallConferenceButton.value
dialerState
)
else {
selectItems.value = uris
@@ -2077,7 +2104,7 @@ private fun callClick(ctx: Context, viewModel: ViewModel, dialerState: ViewModel
ctx,
viewModel,
uris[index],
dialerState.showCallConferenceButton.value
dialerState
)
}
showSelectItemDialog.value = true
@@ -2095,8 +2122,8 @@ private fun callClick(ctx: Context, viewModel: ViewModel, dialerState: ViewModel
}
}
private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String, conferenceCall: Boolean,
onHoldCallp: Long = 0L) {
private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String,
dialerState: ViewModel.DialerState, onHoldCallp: Long = 0L) {
val aor = viewModel.selectedAor.value
val ua = UserAgent.ofAor(aor)!!
val peerUri = if (Utils.isTelNumber(uriText))
@@ -2104,13 +2131,16 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String, confer
else
uriText
val uri = if (Utils.isTelUri(peerUri)) {
if (ua.account.telProvider == "") {
if (dialerState.showCallPstnButton.value)
peerUri
else if (ua.account.telProvider == "") {
alertTitle.value = ctx.getString(R.string.notice)
alertMessage.value = String.format(ctx.getString(R.string.no_telephony_provider), aor)
showAlert.value = true
return
}
Utils.telToSip(peerUri, ua.account)
else
Utils.telToSip(peerUri, ua.account)
}
else
Utils.uriComplete(peerUri, aor)
@@ -2118,28 +2148,62 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String, confer
alertTitle.value = ctx.getString(R.string.notice)
alertMessage.value = String.format(ctx.getString(R.string.invalid_sip_or_tel_uri), uri)
showAlert.value = true
return
}
else if (dialerState.showCallPstnButton.value && !Utils.isTelUri(uri)) {
alertTitle.value = ctx.getString(R.string.notice)
alertMessage.value = "Telephone call can only be made to telephone number"
showAlert.value = true
return
}
else if (Utils.isAudioMode(ctx,AudioManager.MODE_IN_CALL) &&
!Call.calls().any { it.ua.account.aor == ua.account.aor })
Toast.makeText(ctx, R.string.call_already_active, Toast.LENGTH_SHORT).show()
else {
viewModel.dialerState.callButtonsEnabled.value = false
var error = ""
if (BaresipService.telecom) {
val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as android.telecom.TelecomManager
val extras = android.os.Bundle()
extras.putParcelable(android.telecom.TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
BaresipService.getPhoneAccountHandle(ctx))
val callExtras = android.os.Bundle()
callExtras.putBoolean("conferenceCall", conferenceCall)
callExtras.putLong("uap", ua.uap)
if (onHoldCallp != 0L)
callExtras.putLong("onHoldCallp", onHoldCallp)
extras.putBundle(android.telecom.TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras)
try {
Log.d(TAG, "Placing Telecom call to $uri with uap=${ua.uap}")
tm.placeCall(uri.toUri(), extras)
} catch (e: SecurityException) {
Log.e(TAG, "placeCall failed: ${e.message}")
val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
if (dialerState.showCallPstnButton.value) {
val phoneAccountHandle = Utils.pstnAccountHandle(ctx)
if (phoneAccountHandle != null) {
val extras = Bundle().apply {
putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
}
val callExtras = Bundle()
callExtras.putBoolean("pstnCall", dialerState.showCallPstnButton.value)
extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras)
try {
Log.i(TAG, "Placing Telecom PSTN call to $uri with uap=${ua.uap}")
tm.placeCall(uri.toUri(), extras)
} catch (e: SecurityException) {
error = "placeCall failed: ${e.message}"
}
}
else
error = "no phone account"
}
else {
val extras = Bundle()
extras.putParcelable(
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
BaresipService.getPhoneAccountHandle(ctx)
)
val callExtras = Bundle()
callExtras.putBoolean("conferenceCall", dialerState.showCallConferenceButton.value)
callExtras.putLong("uap", ua.uap)
if (onHoldCallp != 0L)
callExtras.putLong("onHoldCallp", onHoldCallp)
extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras)
try {
Log.d(TAG, "Placing Telecom SIP call to $uri with uap=${ua.uap}")
tm.placeCall(uri.toUri(), extras)
} catch (e: SecurityException) {
error = "placeCall failed: ${e.message}"
}
}
if (error != "") {
Log.e(TAG, error)
viewModel.dialerState.callButtonsEnabled.value = true
}
}
@@ -2148,7 +2212,7 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String, confer
intent.action = "Start Call"
intent.putExtra("uap", ua.uap)
intent.putExtra("uri", uri)
intent.putExtra("conferenceCall", conferenceCall)
intent.putExtra("conferenceCall", dialerState.showCallConferenceButton.value)
intent.putExtra("onHoldCallp", onHoldCallp)
ctx.startService(intent)
}
@@ -2207,7 +2271,7 @@ private fun transfer(ctx: Context, viewModel: ViewModel, ua: UserAgent, uriText:
if (success) {
call.onhold = true
call.referTo = uri
makeCall(ctx, viewModel, uri, false, call.callp)
makeCall(ctx, viewModel, uri, viewModel.dialerState, call.callp)
showCall(ctx, viewModel, ua, call)
}
}
@@ -2237,6 +2301,7 @@ private fun showCall(ctx: Context, viewModel: ViewModel, ua: UserAgent?, showCal
viewModel.dialerState.callUriEnabled.value = true
}, 100)
viewModel.dialerState.showCallButton.value = true
viewModel.dialerState.showCallPstnButton.value = true
viewModel.dialerState.showCallConferenceButton.value = true
viewModel.dialerState.callButtonsEnabled.value = true
viewModel.dialerState.showSuggestions.value = false
@@ -2521,7 +2586,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
if (call in Call.calls())
acceptTransfer(ctx, viewModel, ua, call!!, ev[1])
else
makeCall(ctx, viewModel, ev[1], false)
makeCall(ctx, viewModel, ev[1], viewModel.dialerState)
}
showDialog.value = true
}
@@ -2530,7 +2595,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
val call = Call.ofCallp(callp)
if (call in Call.calls())
Api.ua_hangup(uap, callp, 487, "Request Terminated")
makeCall(ctx, viewModel, ev[1], false)
makeCall(ctx, viewModel, ev[1], viewModel.dialerState)
showCall(ctx, viewModel, ua)
}
"transfer failed" -> {