Always use Android Telecom Framework

Automatically add/remove Mobile account when Default Phone App setting is turned on/off
This commit is contained in:
Juha Heinanen
2026-05-11 16:10:11 +03:00
parent 75683291f4
commit 5ec745950d
10 changed files with 90 additions and 212 deletions

View File

@ -285,7 +285,7 @@ class Account(val accp: Long, virtualAor: String? = null) {
BaresipService.filesPath + "/accounts", BaresipService.filesPath + "/accounts",
accounts.toByteArray(Charsets.UTF_8) accounts.toByteArray(Charsets.UTF_8)
) )
Log.d(TAG, "Saved accounts '${accounts}' to '${BaresipService.filesPath}/accounts'") // Log.d(TAG, "Saved accounts '${accounts}' to '${BaresipService.filesPath}/accounts'")
} }
fun ofAor(aor: String): Account? { fun ofAor(aor: String): Account? {

View File

@ -1677,7 +1677,7 @@ private fun checkOnClick(ctx: Context, viewModel: AccountViewModel, ua: UserAgen
if (viewModel.defaultAccount.value) ua.makeDefault() if (viewModel.defaultAccount.value) ua.makeDefault()
Api.account_debug(acc.accp) // Api.account_debug(acc.accp)
Account.saveAccounts() Account.saveAccounts()

View File

@ -164,8 +164,6 @@ private var newOpusPacketLoss = oldOpusPacketLoss
private var newAudioDelay = BaresipService.audioDelay.toString() private var newAudioDelay = BaresipService.audioDelay.toString()
private var newToneCountry = BaresipService.toneCountry private var newToneCountry = BaresipService.toneCountry
private var newRingtoneUri = "" private var newRingtoneUri = ""
private var oldTelecom = BaresipService.telecom
private var newTelecom = oldTelecom
private var save = false private var save = false
@ -178,8 +176,6 @@ private fun AudioContent(contentPadding: PaddingValues) {
oldSpeakerPhone = Config.variable("speaker_phone") == "yes" oldSpeakerPhone = Config.variable("speaker_phone") == "yes"
newSpeakerPhone = oldSpeakerPhone newSpeakerPhone = oldSpeakerPhone
oldTelecom = Config.variable("telecom") == "yes"
newTelecom = oldTelecom
oldAudioModules = Config.variables("module") oldAudioModules = Config.variables("module")
oldOpusBitrate = Config.variable("opus_bitrate") oldOpusBitrate = Config.variable("opus_bitrate")
oldOpusPacketLoss = Config.variable("opus_packet_loss") oldOpusPacketLoss = Config.variable("opus_packet_loss")
@ -206,7 +202,6 @@ private fun AudioContent(contentPadding: PaddingValues) {
.verticalScroll(state = scrollState), .verticalScroll(state = scrollState),
verticalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp),
) { ) {
Telecom()
Ringtone() Ringtone()
ToneCountry() ToneCountry()
SpeakerPhone() SpeakerPhone()
@ -219,37 +214,6 @@ private fun AudioContent(contentPadding: PaddingValues) {
} }
} }
@Composable
private fun Telecom() {
Row(
Modifier
.fillMaxWidth()
.padding(end = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
val telecomTitle = stringResource(R.string.telecom)
val telecomHelp = stringResource(R.string.telecom_help)
Text(text = telecomTitle,
modifier = Modifier
.weight(1f)
.clickable {
alertTitle.value = telecomTitle
alertMessage.value = telecomHelp
showAlert.value = true
},
fontSize = 18.sp)
var telecom by remember { mutableStateOf(oldTelecom) }
Switch(
checked = telecom,
onCheckedChange = {
telecom = it
newTelecom = telecom
}
)
}
}
@Composable @Composable
private fun Ringtone() { private fun Ringtone() {
val ringToneTitle = stringResource(R.string.ringtone) val ringToneTitle = stringResource(R.string.ringtone)
@ -629,13 +593,6 @@ private fun checkOnClick(ctx: Context): Result {
var restart = false var restart = false
if (newTelecom != oldTelecom) {
Config.replaceVariable("telecom", if (newTelecom) "yes" else "no")
BaresipService.telecom = newTelecom
restart = true
save = true
}
if (Preferences(ctx).ringtoneUri != newRingtoneUri) { if (Preferences(ctx).ringtoneUri != newRingtoneUri) {
Preferences(ctx).ringtoneUri = newRingtoneUri Preferences(ctx).ringtoneUri = newRingtoneUri
BaresipService.rt = RingtoneManager.getRingtone(ctx, newRingtoneUri.toUri()) BaresipService.rt = RingtoneManager.getRingtone(ctx, newRingtoneUri.toUri())

View File

@ -1,6 +1,5 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.Manifest
import android.Manifest.permission.RECORD_AUDIO import android.Manifest.permission.RECORD_AUDIO
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Notification import android.app.Notification
@ -61,7 +60,6 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.MessagingStyle import androidx.core.app.NotificationCompat.MessagingStyle
import androidx.core.app.Person import androidx.core.app.Person
@ -491,18 +489,7 @@ class BaresipService: Service() {
activeNetwork = cm.activeNetwork activeNetwork = cm.activeNetwork
Log.i(TAG, "Active network: $activeNetwork") Log.i(TAG, "Active network: $activeNetwork")
if (telecom) registerPhoneAccount()
registerPhoneAccount()
else
if (btAdapter != null) {
Log.i(TAG, "Registering bluetooth receiver")
val filter = IntentFilter()
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)
filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
this.registerReceiver(bluetoothReceiver, filter)
bluetoothReceiverRegistered = true
}
Log.i(TAG, "AEC/AGC/NS available = $aecAvailable/$agcAvailable/$nsAvailable") Log.i(TAG, "AEC/AGC/NS available = $aecAvailable/$agcAvailable/$nsAvailable")
@ -551,8 +538,6 @@ class BaresipService: Service() {
setCallVolume() setCallVolume()
proximitySensing(proximitySensing) proximitySensing(proximitySensing)
call?.answer() call?.answer()
if (!telecom)
ensureCommunicationMode()
} }
"Call Reject" -> { "Call Reject" -> {
@ -877,12 +862,10 @@ class BaresipService: Service() {
proximitySensing(proximitySensing) proximitySensing(proximitySensing)
} }
"call ringing" -> { "call ringing" -> {
if (!telecom) ensureCommunicationMode()
playRingBack() playRingBack()
return return
} }
"call progress" -> { "call progress" -> {
if (!telecom) ensureCommunicationMode()
if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0) if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0)
stopMediaPlayer() stopMediaPlayer()
else { else {
@ -926,7 +909,6 @@ class BaresipService: Service() {
) )
if (resourceId != 0) { if (resourceId != 0) {
ensureCommunicationMode() ensureCommunicationMode()
if (!telecom) playUnInterrupted(resourceId, 1)
} else { } else {
Log.e(TAG, "Callwaiting tone $name.wav not found") Log.e(TAG, "Callwaiting tone $name.wav not found")
} }
@ -946,23 +928,14 @@ class BaresipService: Service() {
Log.d(TAG, "Incoming call $uap/$callp/$peerUri") Log.d(TAG, "Incoming call $uap/$callp/$peerUri")
if (Call.ofCallp(callp) == null) if (Call.ofCallp(callp) == null)
Call(callp, ua, peerUri, "in", "incoming").add() Call(callp, ua, peerUri, "in", "incoming").add()
if (telecom) { val extras = android.os.Bundle()
val extras = android.os.Bundle() extras.putLong("uap", uap)
extras.putLong("uap", uap) extras.putLong("callp", callp)
extras.putLong("callp", callp) extras.putString("peerUri", peerUri)
extras.putString("peerUri", peerUri) try {
try { tm.addNewIncomingCall(getPhoneAccountHandle(this), extras)
tm.addNewIncomingCall(getPhoneAccountHandle(this), extras) } catch (e: Exception) {
} catch (e: Exception) { Log.e(TAG, "Telecom addNewIncomingCall failed: ${e.message}")
Log.e(TAG, "Telecom addNewIncomingCall failed: ${e.message}")
}
} else {
if (!requestAudioFocus(applicationContext)) {
Log.w(TAG, "Audio focus denied for incoming call")
Api.ua_hangup(uap, callp, 486, "Busy Here")
return
}
handleIncomingCall(Call.ofCallp(callp)!!)
} }
return return
} }
@ -1023,12 +996,6 @@ class BaresipService: Service() {
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)
stopMediaPlayer() stopMediaPlayer()
else {
if (!telecom) {
ConnectionService.connections[callp]?.setRinging()
playRingBack()
}
}
} }
if (call.status.value == "connected" && !call.held && !call.onhold) { if (call.status.value == "connected" && !call.held && !call.onhold) {
if (call.callOnHold.value || call.showOnHoldNotice.value) { if (call.callOnHold.value || call.showOnHoldNotice.value) {
@ -1160,13 +1127,8 @@ class BaresipService: Service() {
val tone = ev[2] val tone = ev[2]
if (tone == "busy") if (tone == "busy")
playBusy() playBusy()
else { else
if (!telecom && !Call.inCall())
abandonAudioFocus(applicationContext)
ensureCommunicationMode() ensureCommunicationMode()
}
if (!telecom && !Call.inCall())
abandonAudioFocus(applicationContext)
if (call.dir == "out") if (call.dir == "out")
call.rejected = call.startTime == null && call.rejected = call.startTime == null &&
!reason.startsWith("408") && !reason.startsWith("408") &&
@ -1500,7 +1462,7 @@ class BaresipService: Service() {
fun started() { fun started() {
Log.d(TAG, "Received 'started' from baresip") Log.d(TAG, "Received 'started' from baresip")
isNativeReady = true isNativeReady = true
if (telecom) addMobileUserAgent() addMobileUserAgent()
Api.net_debug() Api.net_debug()
postServiceEvent(ServiceEvent("started", arrayListOf(callActionUri), System.nanoTime())) postServiceEvent(ServiceEvent("started", arrayListOf(callActionUri), System.nanoTime()))
callActionUri = "" callActionUri = ""
@ -1849,23 +1811,41 @@ class BaresipService: Service() {
messageUpdate.postValue(System.currentTimeMillis()) messageUpdate.postValue(System.currentTimeMillis())
} }
private fun addMobileUserAgent() { fun addMobileUserAgent() {
if (!telecom || Utils.pstnAccountHandle(this) == null) return if (VERSION.SDK_INT < 29) return
val mobileAccountHandle = Utils.pstnAccountHandle(this)
val existingMobileUa = uas.value.find { it.account.isMobile }
// If mobile account should not exist (role lost or no SIM), remove it if it exists
if (mobileAccountHandle == null) {
if (existingMobileUa != null) {
Log.d(TAG, "Removing Mobile account (role lost or SIM missing)")
existingMobileUa.remove()
Account.saveAccounts()
}
return
}
val userPart = Utils.getLine1Number(this) ?: "mobile" val userPart = Utils.getLine1Number(this) ?: "mobile"
val mobileAor = "sip:$userPart@pstn" val mobileAor = "sip:$userPart@pstn"
val existingMobileUa = uas.value.find { it.account.isMobile }
if (existingMobileUa != null) { if (existingMobileUa != null) {
// Replace previous sip:mobile@pstn with real number if available // Update AOR if it previously was sip:mobile@pstn but now a real number
if (existingMobileUa.account.aor == "sip:mobile@pstn" && mobileAor != "sip:mobile@pstn") { if (existingMobileUa.account.aor == "sip:mobile@pstn" && mobileAor != "sip:mobile@pstn") {
val updatedUas = uas.value.toMutableList() Log.d(TAG, "Updating existing Mobile account AOR to $mobileAor")
updatedUas.remove(existingMobileUa) val aorField = Account::class.java.getDeclaredField("aor")
uas.value = updatedUas.toList() aorField.isAccessible = true
} else aorField.set(existingMobileUa.account, mobileAor)
return val luriField = Account::class.java.getDeclaredField("luri")
luriField.isAccessible = true
luriField.set(existingMobileUa.account, mobileAor)
Account.saveAccounts()
}
return
} }
Log.d(TAG, "Injecting new virtual Mobile account: $mobileAor")
val account = Account(0L, mobileAor) val account = Account(0L, mobileAor)
account.isMobile = true account.isMobile = true
account.nickName = "Mobile" account.nickName = "Mobile"
@ -2515,7 +2495,6 @@ class BaresipService: Service() {
var isRecOn = false var isRecOn = false
var toneCountry = "us" var toneCountry = "us"
var proximitySensing = true var proximitySensing = true
var telecom = true
val uas = mutableStateOf(emptyList<UserAgent>()) val uas = mutableStateOf(emptyList<UserAgent>())
val uasStatus = mutableStateOf(emptyMap<String, Int>()) val uasStatus = mutableStateOf(emptyMap<String, Int>())
@ -2609,8 +2588,6 @@ class BaresipService: Service() {
) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED ) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
) { ) {
Log.d(TAG, "requestAudioFocus granted") Log.d(TAG, "requestAudioFocus granted")
if (!telecom && isBluetoothHeadsetConnected(ctx))
startBluetoothSco(ctx, 250L, 3)
} }
else { else {
Log.w(TAG, "requestAudioFocus denied") Log.w(TAG, "requestAudioFocus denied")
@ -2619,39 +2596,6 @@ class BaresipService: Service() {
return audioFocusRequest != null return audioFocusRequest != null
} }
fun abandonAudioFocus(ctx: Context) {
val am = ctx.getSystemService(AUDIO_SERVICE) as AudioManager
if (audioFocusRequest != null) {
Log.d(TAG, "Abandoning audio focus")
if (androidx.media.AudioManagerCompat.abandonAudioFocusRequest(
am,
audioFocusRequest!!
) ==
AudioManager.AUDIOFOCUS_REQUEST_GRANTED
) {
audioFocusRequest = null
if (!telecom && isBluetoothHeadsetConnected(ctx))
stopBluetoothSco(ctx)
}
else
Log.e(TAG, "Failed to abandon audio focus")
}
am.mode = MODE_NORMAL
}
private fun isBluetoothHeadsetConnected(ctx: Context): Boolean {
if (VERSION.SDK_INT >= 31 &&
ActivityCompat.checkSelfPermission(
ctx,
Manifest.permission.BLUETOOTH_CONNECT
) == PackageManager.PERMISSION_DENIED
)
return false
return btAdapter != null && btAdapter!!.isEnabled &&
btAdapter!!.getProfileConnectionState(BluetoothHeadset.HEADSET) ==
BluetoothAdapter.STATE_CONNECTED
}
private fun isBluetoothScoOn(am: AudioManager): Boolean { private fun isBluetoothScoOn(am: AudioManager): Boolean {
return if (VERSION.SDK_INT < 31) return if (VERSION.SDK_INT < 31)
@Suppress("DEPRECATION") @Suppress("DEPRECATION")

View File

@ -151,15 +151,11 @@ open class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir
} }
open fun hangup(code: Int, reason: String) { open fun hangup(code: Int, reason: String) {
if (BaresipService.telecom) { val connection = ConnectionService.connections[callp]
val connection = ConnectionService.connections[callp] if (connection != null)
if (connection != null) connection.onDisconnect()
connection.onDisconnect() else
else
Api.ua_hangup(ua.uap, callp, code, reason)
} else {
Api.ua_hangup(ua.uap, callp, code, reason) Api.ua_hangup(ua.uap, callp, code, reason)
}
} }
open fun answer() { open fun answer() {

View File

@ -226,13 +226,6 @@ object Config {
BaresipService.toneCountry = toneCountry BaresipService.toneCountry = toneCountry
config = "${config}tone_country ${BaresipService.toneCountry}\n" config = "${config}tone_country ${BaresipService.toneCountry}\n"
val telecom = previousVariable("telecom")
if (telecom != "")
BaresipService.telecom = telecom == "yes"
else
BaresipService.telecom = true
config = "${config}telecom ${if (BaresipService.telecom) "yes" else "no"}\n"
save() save()
BaresipService.isConfigInitialized = true BaresipService.isConfigInitialized = true

View File

@ -2116,60 +2116,49 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String,
else { else {
viewModel.dialerState.callButtonsEnabled.value = false viewModel.dialerState.callButtonsEnabled.value = false
var error = "" var error = ""
if (BaresipService.telecom) { val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as TelecomManager if (ua.account.isMobile) {
if (ua.account.isMobile) { val phoneAccountHandle = Utils.pstnAccountHandle(ctx)
val phoneAccountHandle = Utils.pstnAccountHandle(ctx) if (phoneAccountHandle != null) {
if (phoneAccountHandle != null) { val extras = Bundle().apply {
val extras = Bundle().apply { putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
}
val callExtras = Bundle()
callExtras.putBoolean("pstnCall", true)
callExtras.putString("aor", aor)
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() val callExtras = Bundle()
callExtras.putBoolean("conferenceCall", dialerState.showCallConferenceButton.value) callExtras.putBoolean("pstnCall", true)
callExtras.putLong("uap", ua.uap) callExtras.putString("aor", aor)
if (onHoldCallp != 0L)
callExtras.putLong("onHoldCallp", onHoldCallp)
extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras) extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras)
try { try {
Log.d(TAG, "Placing Telecom SIP call to $uri with uap=${ua.uap}") Log.i(TAG, "Placing Telecom PSTN call to $uri with uap=${ua.uap}")
tm.placeCall(uri.toUri(), extras) tm.placeCall(uri.toUri(), extras)
} catch (e: SecurityException) { } catch (e: SecurityException) {
error = "placeCall failed: ${e.message}" error = "placeCall failed: ${e.message}"
} }
} }
if (error != "") { else
Log.e(TAG, error) error = "no phone account"
viewModel.dialerState.callButtonsEnabled.value = true
}
} }
else { else {
val intent = Intent(ctx, BaresipService::class.java) val extras = Bundle()
intent.action = "Start Call" extras.putParcelable(
intent.putExtra("uap", ua.uap) TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
intent.putExtra("uri", uri) BaresipService.getPhoneAccountHandle(ctx)
intent.putExtra("conferenceCall", dialerState.showCallConferenceButton.value) )
intent.putExtra("onHoldCallp", onHoldCallp) val callExtras = Bundle()
ctx.startService(intent) 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
} }
} }
} }
@ -2392,8 +2381,6 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
when (ev[0]) { when (ev[0]) {
"call rejected" -> { "call rejected" -> {
if (!BaresipService.telecom)
BaresipService.abandonAudioFocus(ctx)
if (aor == viewModel.selectedAor.value) if (aor == viewModel.selectedAor.value)
viewModel.triggerAccountUpdate() viewModel.triggerAccountUpdate()
} }

View File

@ -80,6 +80,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import com.tutpro.baresip.BaresipService.Companion.uas
import com.tutpro.baresip.CustomElements.AlertDialog import com.tutpro.baresip.CustomElements.AlertDialog
import com.tutpro.baresip.CustomElements.verticalScrollbar import com.tutpro.baresip.CustomElements.verticalScrollbar
import com.tutpro.baresip.Utils.copyInputStreamToFile import com.tutpro.baresip.Utils.copyInputStreamToFile
@ -1031,6 +1032,7 @@ private fun SettingsContent(
) { result -> ) { result ->
Log.d(TAG, "dialerRoleRequest result: $result") Log.d(TAG, "dialerRoleRequest result: $result")
viewModel.defaultDialer.value = roleManager.isRoleHeld(RoleManager.ROLE_DIALER) viewModel.defaultDialer.value = roleManager.isRoleHeld(RoleManager.ROLE_DIALER)
BaresipService.instance?.addMobileUserAgent()
} }
Switch( Switch(
checked = defaultDialer, checked = defaultDialer,
@ -1181,14 +1183,14 @@ private fun SettingsContent(
UserAgent() UserAgent()
UniqueContactUri() UniqueContactUri()
AudioSettings(navController) AudioSettings(navController)
if (VERSION.SDK_INT >= 29)
DefaultDialer()
BatteryOptimizations() BatteryOptimizations()
DarkTheme() DarkTheme()
if (VERSION.SDK_INT >= 31) if (VERSION.SDK_INT >= 31)
DynamicColors() DynamicColors()
ColorBlind() ColorBlind()
ProximitySensing() ProximitySensing()
if (VERSION.SDK_INT >= 29)
DefaultDialer()
Debug() Debug()
SipTrace() SipTrace()
Reset(onRestartApp) Reset(onRestartApp)

View File

@ -4,8 +4,10 @@ import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.KeyguardManager import android.app.KeyguardManager
import android.app.role.RoleManager
import android.content.ContentResolver import android.content.ContentResolver
import android.content.Context import android.content.Context
import android.content.Context.ROLE_SERVICE
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
@ -1335,15 +1337,16 @@ object Utils {
return file return file
} }
@RequiresApi(29)
fun pstnAccountHandle(ctx: Context): PhoneAccountHandle? { fun pstnAccountHandle(ctx: Context): PhoneAccountHandle? {
val roleManager = ctx.getSystemService(ROLE_SERVICE) as RoleManager
if (ctx.checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == if (ctx.checkSelfPermission(Manifest.permission.READ_PHONE_STATE) ==
PackageManager.PERMISSION_GRANTED) { PackageManager.PERMISSION_GRANTED &&
roleManager.isRoleHeld(RoleManager.ROLE_DIALER)) {
val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as TelecomManager val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
if (Build.VERSION.SDK_INT >= 29) { val preferredHandle: PhoneAccountHandle? = tm.userSelectedOutgoingPhoneAccount
val preferredHandle: PhoneAccountHandle? = tm.userSelectedOutgoingPhoneAccount if (preferredHandle != null)
if (preferredHandle != null) return preferredHandle
return preferredHandle
}
val baresipHandle = BaresipService.getPhoneAccountHandle(ctx) val baresipHandle = BaresipService.getPhoneAccountHandle(ctx)
val phoneAccounts = tm.callCapablePhoneAccounts.filter { it != baresipHandle } val phoneAccounts = tm.callCapablePhoneAccounts.filter { it != baresipHandle }
return if (phoneAccounts.isNotEmpty()) return if (phoneAccounts.isNotEmpty())

View File

@ -321,8 +321,7 @@
to standby state.</string> to standby state.</string>
<string name="default_phone_app">Default Phone App</string> <string name="default_phone_app">Default Phone App</string>
<string name="dialer_role_not_available">Dialer role is not available</string> <string name="dialer_role_not_available">Dialer role is not available</string>
<string name="default_phone_app_help">If checked, baresip is the default phone app. Do not check <string name="default_phone_app_help">If checked, baresip is the default phone app.</string>
if your device may need to handle also other than SIP calls or messages.</string>
<string name="listen_address">Listen Address</string> <string name="listen_address">Listen Address</string>
<string name="listen_address_help">IP address and port of form \'address:port\' at which baresip listens <string name="listen_address_help">IP address and port of form \'address:port\' at which baresip listens
for incoming SIP requests. If IP address is an IPv6 address, it must be written inside for incoming SIP requests. If IP address is an IPv6 address, it must be written inside
@ -408,9 +407,6 @@
<string name="colorblind_help">Use colorblind friendly registration status icons</string> <string name="colorblind_help">Use colorblind friendly registration status icons</string>
<string name="proximity_sensing">Proximity Sensing</string> <string name="proximity_sensing">Proximity Sensing</string>
<string name="proximity_sensing_help">If checked, proximity sensing is active during calls.</string> <string name="proximity_sensing_help">If checked, proximity sensing is active during calls.</string>
<string name="telecom">Telecom Framework</string>
<string name="telecom_help">Use Android Telecom framework for calls. If you experience audio
related issues, try if it helps when you turn Telecom Framework off.</string>
<string name="video_size">Video Frame Size</string> <string name="video_size">Video Frame Size</string>
<string name="video_size_help">Size of transmitted video frames (width x height)</string> <string name="video_size_help">Size of transmitted video frames (width x height)</string>
<string name="video_fps">Video Frames Per Second</string> <string name="video_fps">Video Frames Per Second</string>