Mobile account and calling related fixes and enhancements
Search and show also contact URIs in suggestions
This commit is contained in:
@ -892,7 +892,8 @@ class BaresipService: Service() {
|
||||
Blocked.clear(ua.account.aor)
|
||||
BlockRule.clear(ua.account.aor)
|
||||
Api.ua_destroy(uap)
|
||||
if (isNativeReady) Account.saveAccounts()
|
||||
ua.remove()
|
||||
Account.saveAccounts()
|
||||
return
|
||||
}
|
||||
|
||||
@ -903,10 +904,11 @@ class BaresipService: Service() {
|
||||
R.drawable.circle_white
|
||||
else
|
||||
circleRed.getValue(colorblind)
|
||||
} else {
|
||||
}
|
||||
else
|
||||
R.drawable.circle_white
|
||||
}
|
||||
} else if (ua.account.regint == 0)
|
||||
else if (ua.account.regint == 0)
|
||||
R.drawable.circle_white
|
||||
else
|
||||
circleYellow.getValue(colorblind)
|
||||
@ -1076,7 +1078,7 @@ class BaresipService: Service() {
|
||||
"incoming call" -> {
|
||||
if (speakerPhoneAuto)
|
||||
speakerPhone = true
|
||||
val peerUri = ev[1]
|
||||
val peerUri = Utils.uriUnescape(ev[1])
|
||||
var blockedCall = false
|
||||
val toastMsg = if (Call.isAnyCallActive(this))
|
||||
String.format(
|
||||
@ -1143,7 +1145,7 @@ class BaresipService: Service() {
|
||||
}
|
||||
|
||||
"call incoming" -> {
|
||||
val peerUri = ev[1]
|
||||
val peerUri = Utils.uriUnescape(ev[1])
|
||||
Log.d(TAG, "Incoming call $uap/$callp/$peerUri")
|
||||
if (Call.ofCallp(callp) == null)
|
||||
Call(callp, ua, peerUri, "in", "incoming").add()
|
||||
@ -1714,7 +1716,9 @@ class BaresipService: Service() {
|
||||
|
||||
private var audioModeChangedListener: AudioManager.OnModeChangedListener? = null
|
||||
|
||||
fun runCall(uap: Long, uri: String, conferenceCall: Boolean, onHoldCallp: Long) {
|
||||
fun runCall(uap: Long, uriText: String, conferenceCall: Boolean, onHoldCallp: Long) {
|
||||
|
||||
val uri = Utils.uriUnescape(uriText)
|
||||
|
||||
val ua = UserAgent.ofUap(uap)
|
||||
if (ua != null && ua.account.isMobile) {
|
||||
@ -3047,13 +3051,27 @@ class BaresipService: Service() {
|
||||
}
|
||||
|
||||
private fun registerPhoneAccount() {
|
||||
val phoneAccountHandle = getPhoneAccountHandle(this)
|
||||
val phoneAccount = android.telecom.PhoneAccount.builder(phoneAccountHandle, getString(R.string.app_name))
|
||||
.setCapabilities(android.telecom.PhoneAccount.CAPABILITY_SELF_MANAGED)
|
||||
val sipHandle = getPhoneAccountHandle(this, SIP_ACCOUNT_ID)
|
||||
val pstnHandle = getPhoneAccountHandle(this, PSTN_ACCOUNT_ID)
|
||||
|
||||
val sipAccount = android.telecom.PhoneAccount.builder(sipHandle, getString(R.string.app_name))
|
||||
.setCapabilities(android.telecom.PhoneAccount.CAPABILITY_SELF_MANAGED or
|
||||
android.telecom.PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING)
|
||||
.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.mipmap.ic_launcher))
|
||||
.addSupportedUriScheme(android.telecom.PhoneAccount.SCHEME_SIP)
|
||||
.addSupportedUriScheme(android.telecom.PhoneAccount.SCHEME_TEL)
|
||||
.build()
|
||||
tm.registerPhoneAccount(phoneAccount)
|
||||
|
||||
val pstnAccount = android.telecom.PhoneAccount.builder(pstnHandle, getString(R.string.app_name) + " Mobile")
|
||||
.setCapabilities(android.telecom.PhoneAccount.CAPABILITY_CALL_PROVIDER or
|
||||
android.telecom.PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING)
|
||||
.setIcon(android.graphics.drawable.Icon.createWithResource(this, R.mipmap.ic_launcher))
|
||||
.addSupportedUriScheme(android.telecom.PhoneAccount.SCHEME_SIP)
|
||||
.addSupportedUriScheme(android.telecom.PhoneAccount.SCHEME_TEL)
|
||||
.build()
|
||||
|
||||
tm.registerPhoneAccount(sipAccount)
|
||||
tm.registerPhoneAccount(pstnAccount)
|
||||
}
|
||||
|
||||
fun isSimReady(): Boolean {
|
||||
@ -3197,7 +3215,7 @@ class BaresipService: Service() {
|
||||
var instance: BaresipService? = null
|
||||
var isServiceRunning = false
|
||||
var isNativeReady = false
|
||||
var mobileAccount = true
|
||||
var mobileAccount = false
|
||||
var isStartReceived = false
|
||||
var isConfigInitialized = false
|
||||
var libraryLoaded = false
|
||||
@ -3295,11 +3313,12 @@ class BaresipService: Service() {
|
||||
)
|
||||
|
||||
internal const val KEY_TEXT_REPLY = "key_text_reply_baresip"
|
||||
private const val PHONE_ACCOUNT_ID = "baresip_phone_account"
|
||||
const val SIP_ACCOUNT_ID = "baresip_sip_account"
|
||||
const val PSTN_ACCOUNT_ID = "baresip_phone_account"
|
||||
|
||||
fun getPhoneAccountHandle(ctx: Context): PhoneAccountHandle {
|
||||
fun getPhoneAccountHandle(ctx: Context, id: String = SIP_ACCOUNT_ID): PhoneAccountHandle {
|
||||
val componentName = android.content.ComponentName(ctx, ConnectionService::class.java)
|
||||
return PhoneAccountHandle(componentName, PHONE_ACCOUNT_ID)
|
||||
return PhoneAccountHandle(componentName, id)
|
||||
}
|
||||
|
||||
fun setMicMute(mute: Boolean) {
|
||||
|
||||
@ -460,7 +460,8 @@ private fun NewChatPeer(navController: NavController, account: Account) {
|
||||
String.format(noTelephonyProviderText, account.aor)
|
||||
showAlert.value = true
|
||||
""
|
||||
} else
|
||||
}
|
||||
else
|
||||
Utils.telToSip(peerUri, account)
|
||||
}
|
||||
else
|
||||
@ -532,7 +533,7 @@ private fun NewChatPeer(navController: NavController, account: Account) {
|
||||
) {
|
||||
items(
|
||||
items = filteredSuggestions,
|
||||
key = { (contact, _, _) -> "${contact.id()}" }
|
||||
key = { (contact, _, matchingUri) -> "${contact.id()}:${matchingUri?.uri ?: ""}" }
|
||||
) { (contact, annotatedName, matchingUri) ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@ -551,15 +552,17 @@ private fun NewChatPeer(navController: NavController, account: Account) {
|
||||
fontSize = 18.sp
|
||||
)
|
||||
if (matchingUri != null) {
|
||||
val tel = matchingUri.uri.substring(4)
|
||||
val annotatedTel = Utils.buildAnnotatedStringWithHighlight(
|
||||
tel,
|
||||
newPeer.filter { c -> c.isDigit() || c == '+' })
|
||||
val uriPart = matchingUri.uri.substringAfter(":")
|
||||
val highlightPart = if (matchingUri.uri.startsWith("tel:"))
|
||||
newPeer.filter { c -> c.isDigit() || c == '+' }
|
||||
else
|
||||
newPeer
|
||||
val annotatedUri = Utils.buildAnnotatedStringWithHighlight(uriPart, highlightPart)
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
if (matchingUri.label.isNotEmpty())
|
||||
append("${matchingUri.label} ")
|
||||
append(annotatedTel)
|
||||
append(annotatedUri)
|
||||
},
|
||||
fontSize = 14.sp,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
@ -576,31 +579,32 @@ private fun NewChatPeer(navController: NavController, account: Account) {
|
||||
OutlinedTextField(
|
||||
value = newPeer,
|
||||
placeholder = { Text(stringResource(R.string.new_chat_peer)) },
|
||||
onValueChange = {
|
||||
newPeer = it
|
||||
onValueChange = { input ->
|
||||
newPeer = input
|
||||
showSuggestions = newPeer.length > 1
|
||||
filteredSuggestions = if (it.isEmpty())
|
||||
filteredSuggestions = if (input.length <= 1)
|
||||
emptyList()
|
||||
else {
|
||||
val normalizedInput = Utils.unaccent(it)
|
||||
val numericInput = it.filter { c -> c.isDigit() || c == '+' }
|
||||
BaresipService.contacts.mapNotNull { contact ->
|
||||
val normalizedInput = Utils.unaccent(input)
|
||||
val numericInput = input.filter { c -> c.isDigit() || c == '+' }
|
||||
BaresipService.contacts.flatMap { contact ->
|
||||
val nameMatch = Utils.unaccent(contact.name()).contains(normalizedInput, ignoreCase = true)
|
||||
var matchingUri: Contact.ContactUri? = null
|
||||
if (numericInput.isNotEmpty()) {
|
||||
matchingUri = contact.uris().find { u ->
|
||||
u.uri.startsWith("tel:") && u.uri.substring(4).contains(numericInput)
|
||||
val uris = contact.uris()
|
||||
val matchingUris = uris.filter { u ->
|
||||
(u.uri.startsWith("tel:") && numericInput.isNotEmpty() && u.uri.substring(4).contains(numericInput)) ||
|
||||
(u.uri.startsWith("sip:") && u.uri.substring(4).contains(normalizedInput, ignoreCase = true))
|
||||
}
|
||||
}
|
||||
if (nameMatch || matchingUri != null) {
|
||||
val annotatedName = if (nameMatch)
|
||||
Utils.buildAnnotatedStringWithHighlight(contact.name(), it)
|
||||
if (nameMatch) {
|
||||
val annotatedName = Utils.buildAnnotatedStringWithHighlight(contact.name(), input)
|
||||
if (uris.isEmpty())
|
||||
listOf(Triple(contact, annotatedName, null))
|
||||
else
|
||||
AnnotatedString(contact.name())
|
||||
Triple(contact, annotatedName, matchingUri)
|
||||
} else {
|
||||
null
|
||||
uris.map { Triple(contact, annotatedName, it) }
|
||||
}
|
||||
else if (matchingUris.isNotEmpty())
|
||||
matchingUris.map { Triple(contact, AnnotatedString(contact.name()), it) }
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -643,9 +647,9 @@ private fun NewChatPeer(navController: NavController, account: Account) {
|
||||
else
|
||||
String.format(contactNoSipOrTelUriText, peerText)
|
||||
showAlert.value = true
|
||||
} else {
|
||||
makeChat(navController, account, peerText)
|
||||
}
|
||||
else
|
||||
makeChat(navController, account, peerText)
|
||||
}
|
||||
else if (uris.size == 1)
|
||||
makeChat(navController, account, uris[0].uri)
|
||||
|
||||
@ -44,14 +44,13 @@ class ConnectionService : ConnectionService() {
|
||||
@Suppress("DEPRECATION")
|
||||
val currentRoute = it.callAudioState?.route ?: CallAudioState.ROUTE_EARPIECE
|
||||
@Suppress("DEPRECATION")
|
||||
if (speaker) {
|
||||
if (speaker)
|
||||
it.setAudioRoute(CallAudioState.ROUTE_SPEAKER)
|
||||
} else if (currentRoute == CallAudioState.ROUTE_SPEAKER) {
|
||||
else if (currentRoute == CallAudioState.ROUTE_SPEAKER)
|
||||
it.setAudioRoute(CallAudioState.ROUTE_EARPIECE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateIncomingConnection(
|
||||
connectionManagerPhoneAccount: PhoneAccountHandle?,
|
||||
@ -72,6 +71,8 @@ class ConnectionService : ConnectionService() {
|
||||
Connection.CAPABILITY_HOLD or
|
||||
Connection.CAPABILITY_MERGE_CONFERENCE or
|
||||
Connection.CAPABILITY_SWAP_CONFERENCE
|
||||
if (request?.accountHandle?.id == BaresipService.SIP_ACCOUNT_ID)
|
||||
connection.connectionProperties = Connection.PROPERTY_SELF_MANAGED
|
||||
connection.audioModeIsVoip = true
|
||||
|
||||
val call = Call.ofCallp(callp)
|
||||
@ -110,9 +111,14 @@ class ConnectionService : ConnectionService() {
|
||||
val rootExtras = request?.extras
|
||||
val nestedExtras = rootExtras?.getBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)
|
||||
|
||||
val uap = rootExtras?.getLong("uap", 0L).takeIf { it != 0L }
|
||||
var uap = rootExtras?.getLong("uap", 0L).takeIf { it != 0L }
|
||||
?: nestedExtras?.getLong("uap") ?: 0L
|
||||
|
||||
if (uap == 0L && BaresipService.uas.value.isNotEmpty()) {
|
||||
uap = BaresipService.uas.value[0].uap
|
||||
Log.d(TAG, "Outgoing connection request from system, using default uap $uap")
|
||||
}
|
||||
|
||||
val conferenceCall = rootExtras?.getBoolean("conferenceCall", false) ?:
|
||||
nestedExtras?.getBoolean("conferenceCall") ?: false
|
||||
|
||||
@ -122,7 +128,7 @@ class ConnectionService : ConnectionService() {
|
||||
val onHoldCallp = rootExtras?.getLong("onHoldCallp", 0L).takeIf { it != 0L }
|
||||
?: nestedExtras?.getLong("onHoldCallp") ?: 0L
|
||||
|
||||
val destination = request?.address?.encodedSchemeSpecificPart ?: ""
|
||||
val destination = request?.address?.schemeSpecificPart ?: ""
|
||||
|
||||
Log.d(TAG, "onCreateOutgoingConnection to $destination (uap=$uap)")
|
||||
|
||||
@ -143,6 +149,8 @@ class ConnectionService : ConnectionService() {
|
||||
Connection.CAPABILITY_HOLD or
|
||||
Connection.CAPABILITY_MERGE_CONFERENCE or
|
||||
Connection.CAPABILITY_SWAP_CONFERENCE
|
||||
if (request?.accountHandle?.id == BaresipService.SIP_ACCOUNT_ID)
|
||||
connection.connectionProperties = Connection.PROPERTY_SELF_MANAGED
|
||||
|
||||
if (!pstnCall) {
|
||||
connection.audioModeIsVoip = true
|
||||
@ -225,9 +233,9 @@ class ConnectionService : ConnectionService() {
|
||||
if (callp != 0L) {
|
||||
Api.ua_hangup(uap, callp, 0, "")
|
||||
connections.remove(callp)
|
||||
} else {
|
||||
pendingOutgoingConnection = null
|
||||
}
|
||||
else
|
||||
pendingOutgoingConnection = null
|
||||
setDisconnected(DisconnectCause(DisconnectCause.CANCELED))
|
||||
destroy()
|
||||
}
|
||||
@ -281,9 +289,9 @@ class ConnectionService : ConnectionService() {
|
||||
call.showOnHoldNotice.value = true
|
||||
// 3. Tell Telecom the move is complete
|
||||
setOnHold()
|
||||
} else {
|
||||
Log.e(TAG, "SIP Hold failed for $callp")
|
||||
}
|
||||
else
|
||||
Log.e(TAG, "SIP Hold failed for $callp")
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,9 +307,9 @@ class ConnectionService : ConnectionService() {
|
||||
call.showOnHoldNotice.value = false
|
||||
// 3. Tell Telecom we are active
|
||||
setActive()
|
||||
} else {
|
||||
Log.e(TAG, "SIP Resume failed for $callp")
|
||||
}
|
||||
else
|
||||
Log.e(TAG, "SIP Resume failed for $callp")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -18,9 +18,10 @@ class InCallService : InCallService() {
|
||||
Log.d(TAG, "InCallService: Call added")
|
||||
|
||||
val handle = call.details.accountHandle
|
||||
val baresipHandle = BaresipService.getPhoneAccountHandle(this)
|
||||
val baresipSipHandle = BaresipService.getPhoneAccountHandle(this, BaresipService.SIP_ACCOUNT_ID)
|
||||
val baresipPstnHandle = BaresipService.getPhoneAccountHandle(this, BaresipService.PSTN_ACCOUNT_ID)
|
||||
|
||||
if (handle == baresipHandle) {
|
||||
if (handle == baresipSipHandle || handle == baresipPstnHandle) {
|
||||
Log.d(TAG, "InCallService: Identified as SIP call")
|
||||
// SIP call is already managed by ConnectionService/BaresipService
|
||||
}
|
||||
|
||||
@ -169,6 +169,8 @@ import com.tutpro.baresip.CustomElements.PasswordDialog
|
||||
import com.tutpro.baresip.CustomElements.SelectableAlertDialog
|
||||
import com.tutpro.baresip.CustomElements.verticalScrollbar
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
@ -1204,38 +1206,39 @@ private fun CallUriRow(
|
||||
value = if (isDialer) dialerState.callUri.value else call!!.callUri.value,
|
||||
readOnly = if (isDialer) !dialerState.callUriEnabled.value else !call!!.callUriEnabled.value,
|
||||
singleLine = true,
|
||||
onValueChange = {
|
||||
onValueChange = { input ->
|
||||
if (isDialer)
|
||||
if (it != dialerState.callUri.value) {
|
||||
dialerState.callUri.value = it
|
||||
if (input != dialerState.callUri.value) {
|
||||
dialerState.callUri.value = input
|
||||
dialerState.redialUri = ""
|
||||
if (it == "") {
|
||||
if (input == "") {
|
||||
dialerState.showCallButton.value = true
|
||||
dialerState.showCallConferenceButton.value = true
|
||||
}
|
||||
if (it.length > 1) {
|
||||
val normalizedInput = Utils.unaccent(it)
|
||||
val numericInput = it.filter { c -> c.isDigit() || c == '+' }
|
||||
filteredSuggestions = BaresipService.contacts.mapNotNull { contact ->
|
||||
if (input.length > 1) {
|
||||
val normalizedInput = Utils.unaccent(input)
|
||||
val numericInput = input.filter { c -> c.isDigit() || c == '+' }
|
||||
filteredSuggestions = BaresipService.contacts.flatMap { contact ->
|
||||
val nameMatch = Utils.unaccent(contact.name()).contains(normalizedInput, ignoreCase = true)
|
||||
var matchingUri: Contact.ContactUri? = null
|
||||
if (numericInput.isNotEmpty()) {
|
||||
matchingUri = contact.uris().find { u ->
|
||||
u.uri.startsWith("tel:") && u.uri.substring(4).contains(numericInput)
|
||||
val uris = contact.uris()
|
||||
val matchingUris = uris.filter { u ->
|
||||
(u.uri.startsWith("tel:") && numericInput.isNotEmpty() && u.uri.substring(4).contains(numericInput)) ||
|
||||
(u.uri.startsWith("sip:") && u.uri.substring(4).contains(normalizedInput, ignoreCase = true))
|
||||
}
|
||||
}
|
||||
if (nameMatch || matchingUri != null) {
|
||||
val annotatedName = if (nameMatch)
|
||||
Utils.buildAnnotatedStringWithHighlight(contact.name(), it)
|
||||
if (nameMatch) {
|
||||
val annotatedName = Utils.buildAnnotatedStringWithHighlight(contact.name(), input)
|
||||
if (uris.isEmpty())
|
||||
listOf(Triple(contact, annotatedName, null))
|
||||
else
|
||||
AnnotatedString(contact.name())
|
||||
Triple(contact, annotatedName, matchingUri)
|
||||
} else {
|
||||
null
|
||||
uris.map { Triple(contact, annotatedName, it) }
|
||||
}
|
||||
else if (matchingUris.isNotEmpty())
|
||||
matchingUris.map { Triple(contact, AnnotatedString(contact.name()), it) }
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
dialerState.showSuggestions.value = it.length > 1
|
||||
dialerState.showSuggestions.value = input.length > 1
|
||||
}
|
||||
},
|
||||
trailingIcon = {
|
||||
@ -1329,7 +1332,7 @@ private fun CallUriRow(
|
||||
) {
|
||||
items(
|
||||
items = filteredSuggestions,
|
||||
key = { (contact, _, _) -> "${contact.id()}" }
|
||||
key = { (contact, _, matchingUri) -> "${contact.id()}:${matchingUri?.uri ?: ""}" }
|
||||
) { (contact, annotatedName, matchingUri) ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@ -1348,15 +1351,17 @@ private fun CallUriRow(
|
||||
fontSize = 18.sp
|
||||
)
|
||||
if (matchingUri != null) {
|
||||
val tel = matchingUri.uri.substring(4)
|
||||
val annotatedTel = Utils.buildAnnotatedStringWithHighlight(
|
||||
tel,
|
||||
dialerState.callUri.value.filter { c -> c.isDigit() || c == '+' })
|
||||
val uriPart = matchingUri.uri.substringAfter(":")
|
||||
val highlightPart = if (matchingUri.uri.startsWith("tel:"))
|
||||
dialerState.callUri.value.filter { c -> c.isDigit() || c == '+' }
|
||||
else
|
||||
dialerState.callUri.value
|
||||
val annotatedUri = Utils.buildAnnotatedStringWithHighlight(uriPart, highlightPart)
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
if (matchingUri.label.isNotEmpty())
|
||||
append("${matchingUri.label} ")
|
||||
append(annotatedTel)
|
||||
append(annotatedUri)
|
||||
},
|
||||
fontSize = 14.sp,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
@ -1664,29 +1669,30 @@ private fun CallRow(
|
||||
OutlinedTextField(
|
||||
value = transferUri,
|
||||
singleLine = true,
|
||||
onValueChange = {
|
||||
if (it != transferUri) {
|
||||
transferUri = it
|
||||
if (it.length > 1) {
|
||||
val normalizedInput = Utils.unaccent(it)
|
||||
val numericInput = it.filter { c -> c.isDigit() || c == '+' }
|
||||
filteredSuggestions = BaresipService.contacts.mapNotNull { contact ->
|
||||
onValueChange = { input ->
|
||||
if (input != transferUri) {
|
||||
transferUri = input
|
||||
if (input.length > 1) {
|
||||
val normalizedInput = Utils.unaccent(input)
|
||||
val numericInput = input.filter { c -> c.isDigit() || c == '+' }
|
||||
filteredSuggestions = BaresipService.contacts.flatMap { contact ->
|
||||
val nameMatch = Utils.unaccent(contact.name()).contains(normalizedInput, ignoreCase = true)
|
||||
var matchingUri: Contact.ContactUri? = null
|
||||
if (numericInput.isNotEmpty()) {
|
||||
matchingUri = contact.uris().find { u ->
|
||||
u.uri.startsWith("tel:") && u.uri.substring(4).contains(numericInput)
|
||||
val uris = contact.uris()
|
||||
val matchingUris = uris.filter { u ->
|
||||
(u.uri.startsWith("tel:") && numericInput.isNotEmpty() && u.uri.substring(4).contains(numericInput)) ||
|
||||
(u.uri.startsWith("sip:") && u.uri.substring(4).contains(normalizedInput, ignoreCase = true))
|
||||
}
|
||||
}
|
||||
if (nameMatch || matchingUri != null) {
|
||||
val annotatedName = if (nameMatch)
|
||||
Utils.buildAnnotatedStringWithHighlight(contact.name(), it)
|
||||
if (nameMatch) {
|
||||
val annotatedName = Utils.buildAnnotatedStringWithHighlight(contact.name(), input)
|
||||
if (uris.isEmpty())
|
||||
listOf(Triple(contact, annotatedName, null))
|
||||
else
|
||||
AnnotatedString(contact.name())
|
||||
Triple(contact, annotatedName, matchingUri)
|
||||
} else {
|
||||
null
|
||||
uris.map { Triple(contact, annotatedName, it) }
|
||||
}
|
||||
else if (matchingUris.isNotEmpty())
|
||||
matchingUris.map { Triple(contact, AnnotatedString(contact.name()), it) }
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
call.showSuggestions.value = transferUri.length > 1
|
||||
@ -1739,7 +1745,7 @@ private fun CallRow(
|
||||
) {
|
||||
items(
|
||||
items = filteredSuggestions,
|
||||
key = { (contact, _, _) -> "${contact.id()}" }
|
||||
key = { (contact, _, matchingUri) -> "${contact.id()}:${matchingUri?.uri ?: ""}" }
|
||||
) { (contact, annotatedName, matchingUri) ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@ -1758,15 +1764,17 @@ private fun CallRow(
|
||||
fontSize = 18.sp
|
||||
)
|
||||
if (matchingUri != null) {
|
||||
val tel = matchingUri.uri.substring(4)
|
||||
val annotatedTel = Utils.buildAnnotatedStringWithHighlight(
|
||||
tel,
|
||||
transferUri.filter { c -> c.isDigit() || c == '+' })
|
||||
val uriPart = matchingUri.uri.substringAfter(":")
|
||||
val highlightPart = if (matchingUri.uri.startsWith("tel:"))
|
||||
transferUri.filter { c -> c.isDigit() || c == '+' }
|
||||
else
|
||||
transferUri
|
||||
val annotatedUri = Utils.buildAnnotatedStringWithHighlight(uriPart, highlightPart)
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
if (matchingUri.label.isNotEmpty())
|
||||
append("${matchingUri.label} ")
|
||||
append(annotatedTel)
|
||||
append(annotatedUri)
|
||||
},
|
||||
fontSize = 14.sp,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
@ -2150,6 +2158,14 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String,
|
||||
extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras)
|
||||
try {
|
||||
Log.i(TAG, "Placing Telecom PSTN call to $uri with uap=${ua.uap}")
|
||||
val extras = Bundle().apply {
|
||||
putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
|
||||
BaresipService.getPhoneAccountHandle(ctx, BaresipService.PSTN_ACCOUNT_ID))
|
||||
}
|
||||
val callExtras = Bundle()
|
||||
callExtras.putBoolean("pstnCall", true)
|
||||
callExtras.putString("aor", aor)
|
||||
extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras)
|
||||
val telecomUri = if (uri.startsWith("tel:"))
|
||||
Uri.fromParts("tel", uri.substring(4), null)
|
||||
else
|
||||
@ -2166,7 +2182,7 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String,
|
||||
val extras = Bundle()
|
||||
extras.putParcelable(
|
||||
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
|
||||
BaresipService.getPhoneAccountHandle(ctx)
|
||||
BaresipService.getPhoneAccountHandle(ctx, BaresipService.SIP_ACCOUNT_ID)
|
||||
)
|
||||
val callExtras = Bundle()
|
||||
callExtras.putBoolean("conferenceCall", dialerState.showCallConferenceButton.value)
|
||||
@ -2185,6 +2201,14 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String,
|
||||
Log.e(TAG, error)
|
||||
viewModel.dialerState.callButtonsEnabled.value = true
|
||||
}
|
||||
else
|
||||
viewModel.viewModelScope.launch {
|
||||
delay(5000.milliseconds)
|
||||
if (Call.calls().isEmpty()) {
|
||||
Log.d(TAG, "Re-enabling dialer buttons after timeout (no calls)")
|
||||
viewModel.dialerState.callButtonsEnabled.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2467,7 +2491,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
|
||||
spinToAor(viewModel, aor, Call.ofCallp(callp))
|
||||
}
|
||||
"call redirect" -> {
|
||||
val redirectUri = ev[1]
|
||||
val redirectUri = Utils.uriUnescape(ev[1])
|
||||
val target = Utils.friendlyUri(ctx, redirectUri, acc)
|
||||
if (acc.autoRedirect) {
|
||||
redirect(ctx, viewModel, ua, redirectUri)
|
||||
@ -2545,7 +2569,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
|
||||
viewModel.navigateToHome()
|
||||
val callp = params[1] as Long
|
||||
val call = Call.ofCallp(callp)
|
||||
val target = Utils.friendlyUri(ctx, ev[1], acc)
|
||||
val target = Utils.friendlyUri(ctx, Utils.uriUnescape(ev[1]), acc)
|
||||
dialogTitle.value = if (call != null)
|
||||
ctx.getString(R.string.transfer_request)
|
||||
else
|
||||
|
||||
@ -212,10 +212,12 @@ object Utils {
|
||||
return u
|
||||
}
|
||||
|
||||
fun e164Uri(uri: String, countryCode: String): String {
|
||||
fun e164Uri(uriText: String, countryCode: String): String {
|
||||
val uri = uriUnescape(uriText)
|
||||
val scheme = uri.take(4)
|
||||
val userPart = uriUserPart(uri)
|
||||
return if (userPart.isNotEmpty() && userPart.isDigitsOnly())
|
||||
val digitsOnlyUserPart = userPart.filter { it.isDigit() }
|
||||
return if (digitsOnlyUserPart.isNotEmpty() && digitsOnlyUserPart.length == userPart.filterNot { it == ' ' || it == '-' || it == '(' || it == ')' || it == '+' }.length)
|
||||
when {
|
||||
userPart.startsWith("00") -> uri.replace("$scheme$userPart",
|
||||
scheme + "+" + userPart.substring(2))
|
||||
@ -238,7 +240,7 @@ object Utils {
|
||||
|
||||
fun uriUnescape(uri: String): String {
|
||||
return uri.replace("%2B" to "+", "%3A" to ":", "%3B" to ";", "%40" to "@", "%3D" to "=",
|
||||
"%23" to "#", "%2A" to "*")
|
||||
"%23" to "#", "%2A" to "*", "%20" to "")
|
||||
}
|
||||
|
||||
fun aorDomain(aor: String): String {
|
||||
|
||||
Reference in New Issue
Block a user