diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt
index 7ecf2af9..7ac9d00b 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt
@@ -1653,6 +1653,12 @@ class BaresipService: Service() {
fun runCall(uap: Long, uri: String, conferenceCall: Boolean, onHoldCallp: Long) {
+ val ua = UserAgent.ofUap(uap)
+ if (ua != null && ua.account.isMobile && Utils.isUssd(uri)) {
+ sendUssd(Utils.uriUserPart(uri))
+ return
+ }
+
val executeCall = {
val handler = Handler(Looper.getMainLooper())
handler.postDelayed({
@@ -2312,6 +2318,47 @@ class BaresipService: Service() {
updateMobileStatus(status)
}
+ @SuppressLint("MissingPermission")
+ private fun sendUssd(ussdCode: String) {
+ val uap = uas.value.find { it.account.isMobile }?.uap ?: 0L
+ if (VERSION.SDK_INT >= 26) {
+ val tm = getSystemService(TELEPHONY_SERVICE) as TelephonyManager
+ try {
+ tm.sendUssdRequest(ussdCode, object : TelephonyManager.UssdResponseCallback() {
+ override fun onReceiveUssdResponse(
+ telephonyManager: TelephonyManager,
+ request: String,
+ response: CharSequence
+ ) {
+ super.onReceiveUssdResponse(telephonyManager, request, response)
+ Log.d(TAG, "USSD response for $request: $response")
+ postServiceEvent(ServiceEvent("ussd response", arrayListOf(uap, request, response.toString()), System.nanoTime()))
+ }
+
+ override fun onReceiveUssdResponseFailed(
+ telephonyManager: TelephonyManager,
+ request: String,
+ failureCode: Int
+ ) {
+ super.onReceiveUssdResponseFailed(telephonyManager, request, failureCode)
+ Log.d(TAG, "USSD response for $request failed with code $failureCode")
+ val error = when (failureCode) {
+ TelephonyManager.USSD_RETURN_FAILURE -> getString(R.string.ussd_failed)
+ TelephonyManager.USSD_ERROR_SERVICE_UNAVAIL -> getString(R.string.ussd_service_unavailable)
+ else -> getString(R.string.ussd_unknown_error)
+ }
+ postServiceEvent(ServiceEvent("ussd fail", arrayListOf(uap, request, error), System.nanoTime()))
+ }
+ }, Handler(Looper.getMainLooper()))
+ } catch (e: Exception) {
+ Log.e(TAG, "Exception sending USSD request: ${e.message}")
+ toast(getString(R.string.ussd_failed))
+ }
+ } else {
+ toast(getString(R.string.ussd_not_supported))
+ }
+ }
+
@SuppressLint("FullScreenIntentPolicy")
fun handleIncomingCall(call: Call) {
val ua = call.ua
diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt
index 9769efce..336be445 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt
@@ -2057,6 +2057,14 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String,
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 if (ua.account.isMobile && Utils.isUssd(uri)) {
+ viewModel.dialerState.callButtonsEnabled.value = false
+ val baresipService = Intent(ctx, BaresipService::class.java)
+ baresipService.action = "Start Call"
+ baresipService.putExtra("uap", ua.uap)
+ baresipService.putExtra("uri", uri)
+ ContextCompat.startForegroundService(ctx, baresipService)
+ }
else {
viewModel.dialerState.callButtonsEnabled.value = false
var error = ""
@@ -2330,6 +2338,20 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
val aor = ua.account.aor
when (ev[0]) {
+ "ussd response" -> {
+ if (aor == viewModel.selectedAor.value)
+ viewModel.dialerState.callButtonsEnabled.value = true
+ handleDialog(ctx, ctx.getString(R.string.info), params[2] as String)
+ handleNextEvent()
+ return
+ }
+ "ussd fail" -> {
+ if (aor == viewModel.selectedAor.value)
+ viewModel.dialerState.callButtonsEnabled.value = true
+ handleDialog(ctx, ctx.getString(R.string.error), params[2] as String)
+ handleNextEvent()
+ return
+ }
"call rejected" -> {}
"call outgoing" -> {
val callp = params[1] as Long
diff --git a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt
index 5f190b39..b7adc48d 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt
@@ -370,13 +370,18 @@ object Utils {
}
fun isTelNumber(no: String): Boolean {
- return no.isNotEmpty() && Regex("^([+][1-9])?[0-9- (),*#]{0,24}$").matches(no)
+ return no.isNotEmpty() && Regex("^[0-9- (),*#+]{1,32}$").matches(no)
}
fun isTelUri(uri: String): Boolean {
return uri.startsWith("tel:") && isTelNumber(uri.substring(4))
}
+ fun isUssd(uri: String): Boolean {
+ val user = uriUserPart(uri)
+ return user.startsWith("*") && user.endsWith("#")
+ }
+
fun checkUri(uri: String): Boolean {
return checkSipUri(uri) || isTelUri(uri)
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 717aa2d5..14f4083e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -633,6 +633,10 @@
No network connection!
No hardware Acoustic Echo Cancellation!
Audio focus denied!
+ USSD failed
+ USSD service unavailable
+ USSD unknown error
+ USSD not supported
Permissions rationale
baresip needs \"Microphone\" permission for voice calls,
\"Nearby devices\" permission for Bluetooth microphone/speaker detection,