Added Telephony Provider account configuration item

Added support for Contact tel URIs
Show account's STUN configuration items only if needed
Avoid some lint warnings in baresip.c
This commit is contained in:
Juha Heinanen
2022-02-02 11:36:08 +02:00
parent c285719da1
commit d3a2282f03
12 changed files with 291 additions and 156 deletions
+2 -2
View File
@@ -362,8 +362,8 @@ static struct mqueue *mq;
static void mqueue_handler(int id, void *data, void *arg) static void mqueue_handler(int id, void *data, void *arg)
{ {
if (id == ID_UA_STOP_ALL) { if (id == ID_UA_STOP_ALL) {
LOGD("calling ua_stop_all with force %u\n", (unsigned)data); LOGD("calling ua_stop_all with force %u\n", (unsigned)(uintptr_t)data);
ua_stop_all((bool)data); ua_stop_all((bool)(uintptr_t)data);
} }
} }
@@ -3,6 +3,8 @@ package com.tutpro.baresip
import android.content.Context import android.content.Context
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import java.net.URLEncoder
import java.net.URLDecoder
class Account(val accp: String) { class Account(val accp: String) {
@@ -28,6 +30,7 @@ class Account(val accp: String) {
var missedCalls = false var missedCalls = false
var unreadMessages = false var unreadMessages = false
var callHistory = true var callHistory = true
var telProvider = Utils.aorDomain(aor)
var resumeUri = "" var resumeUri = ""
init { init {
@@ -56,6 +59,10 @@ class Account(val accp: String) {
val extra = Api.account_extra(accp) val extra = Api.account_extra(accp)
callHistory = Utils.paramValue(extra,"call_history") == "" callHistory = Utils.paramValue(extra,"call_history") == ""
telProvider = if (Utils.paramExists(extra, "tel_provider"))
URLDecoder.decode(Utils.paramValue(extra,"tel_provider"), "UTF-8")
else
Utils.aorDomain(aor)
} }
@@ -119,6 +126,8 @@ class Account(val accp: String) {
if (!callHistory) if (!callHistory)
extra += ";call_history=no" extra += ";call_history=no"
extra += ";tel_provider=${URLEncoder.encode(telProvider, "UTF-8")}"
if (extra != "") if (extra != "")
res += ";extra=\"" + extra.substringAfter(";") + "\"" res += ";extra=\"" + extra.substringAfter(";") + "\""
@@ -2,10 +2,13 @@ package com.tutpro.baresip
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.opengl.Visibility
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.* import android.widget.*
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.tutpro.baresip.databinding.ActivityAccountBinding import com.tutpro.baresip.databinding.ActivityAccountBinding
@@ -33,6 +36,7 @@ class AccountActivity : AppCompatActivity() {
private lateinit var outbound1: EditText private lateinit var outbound1: EditText
private lateinit var outbound2: EditText private lateinit var outbound2: EditText
private lateinit var mediaNat: String private lateinit var mediaNat: String
private lateinit var stun: LinearLayout
private lateinit var stunServer: EditText private lateinit var stunServer: EditText
private lateinit var stunUser: EditText private lateinit var stunUser: EditText
private lateinit var stunPass: EditText private lateinit var stunPass: EditText
@@ -45,6 +49,7 @@ class AccountActivity : AppCompatActivity() {
private var answerMode = Api.ANSWERMODE_MANUAL private var answerMode = Api.ANSWERMODE_MANUAL
private lateinit var answerModeSpinner: Spinner private lateinit var answerModeSpinner: Spinner
private lateinit var vmUri: EditText private lateinit var vmUri: EditText
private lateinit var telProvider: EditText
private lateinit var defaultCheck: CheckBox private lateinit var defaultCheck: CheckBox
private val mediaEncKeys = arrayListOf("zrtp", "dtls_srtp", "srtp-mandf", "srtp-mand", "srtp", "") private val mediaEncKeys = arrayListOf("zrtp", "dtls_srtp", "srtp-mandf", "srtp-mand", "srtp", "")
@@ -73,6 +78,7 @@ class AccountActivity : AppCompatActivity() {
outbound2 = binding.Outbound2 outbound2 = binding.Outbound2
regCheck = binding.Register regCheck = binding.Register
mediaNatSpinner = binding.mediaNatSpinner mediaNatSpinner = binding.mediaNatSpinner
stun = binding.Stun
stunServer = binding.StunServer stunServer = binding.StunServer
stunUser = binding.StunUser stunUser = binding.StunUser
stunPass = binding.StunPass stunPass = binding.StunPass
@@ -80,6 +86,7 @@ class AccountActivity : AppCompatActivity() {
dtmfModeSpinner = binding.dtmfModeSpinner dtmfModeSpinner = binding.dtmfModeSpinner
answerModeSpinner = binding.answerModeSpinner answerModeSpinner = binding.answerModeSpinner
vmUri = binding.voicemailUri vmUri = binding.voicemailUri
telProvider = binding.telephonyProvider
defaultCheck = binding.Default defaultCheck = binding.Default
aor = intent.getStringExtra("aor")!! aor = intent.getStringExtra("aor")!!
@@ -230,27 +237,21 @@ class AccountActivity : AppCompatActivity() {
stunServer.setText(resources.getString(R.string.stun_server_default)) stunServer.setText(resources.getString(R.string.stun_server_default))
else if ((mediaNat == "ice") && (stunServer.text.toString() == "")) else if ((mediaNat == "ice") && (stunServer.text.toString() == ""))
stunServer.setText(resources.getString(R.string.stun_server_default)) stunServer.setText(resources.getString(R.string.stun_server_default))
else if (mediaNat == "") { if (mediaNat == "") {
stun.visibility = GONE
stunServer.setText("") stunServer.setText("")
stunUser.setText("") stunUser.setText("")
stunPass.setText("") stunPass.setText("")
} } else
stunServer.isEnabled = mediaNat != "" stun.visibility = VISIBLE
stunUser.isEnabled = mediaNat != ""
stunPass.isEnabled = mediaNat != ""
} }
override fun onNothingSelected(parent: AdapterView<*>) { override fun onNothingSelected(parent: AdapterView<*>) {
} }
} }
stunServer.setText(acc.stunServer) stunServer.setText(acc.stunServer)
stunServer.isEnabled = mediaNat != ""
stunUser.setText(acc.stunUser) stunUser.setText(acc.stunUser)
stunUser.isEnabled = mediaNat != ""
stunPass.setText(acc.stunPass) stunPass.setText(acc.stunPass)
stunPass.isEnabled = mediaNat != ""
mediaEnc = acc.mediaEnc mediaEnc = acc.mediaEnc
keyIx = mediaEncKeys.indexOf(mediaEnc) keyIx = mediaEncKeys.indexOf(mediaEnc)
@@ -313,6 +314,8 @@ class AccountActivity : AppCompatActivity() {
} }
} }
telProvider.setText(acc.telProvider)
vmUri.setText(acc.vmUri) vmUri.setText(acc.vmUri)
defaultCheck.isChecked = uaIndex == 0 defaultCheck.isChecked = uaIndex == 0
@@ -563,9 +566,9 @@ class AccountActivity : AppCompatActivity() {
if (tVmUri != "") { if (tVmUri != "") {
if (!tVmUri.startsWith("sip:")) tVmUri = "sip:$tVmUri" if (!tVmUri.startsWith("sip:")) tVmUri = "sip:$tVmUri"
if (!tVmUri.contains("@")) tVmUri = "$tVmUri@${acc.host()}" if (!tVmUri.contains("@")) tVmUri = "$tVmUri@${acc.host()}"
if (!Utils.checkSipUri(tVmUri)) { if (!Utils.checkUri(tVmUri)) {
Utils.alertView(this, getString(R.string.notice), Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_voicemail_uri), tVmUri)) String.format(getString(R.string.invalid_sip_or_tel_uri), tVmUri))
return false return false
} }
Api.account_set_mwi(acc.accp, "yes") Api.account_set_mwi(acc.accp, "yes")
@@ -576,6 +579,17 @@ class AccountActivity : AppCompatActivity() {
save = true save = true
} }
val hostPart = telProvider.text.toString().trim()
if (hostPart != acc.telProvider) {
if (hostPart != "" && !Utils.checkHostPortParams(hostPart)) {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_sip_uri_hostpart), hostPart))
return false
}
acc.telProvider = hostPart
save = true
}
if (defaultCheck.isChecked && (uaIndex > 0)) { if (defaultCheck.isChecked && (uaIndex > 0)) {
val uasTmp = BaresipService.uas[0] val uasTmp = BaresipService.uas[0]
val statusTmp = BaresipService.status[0] val statusTmp = BaresipService.status[0]
@@ -708,6 +722,12 @@ class AccountActivity : AppCompatActivity() {
getString(R.string.voicemain_uri_help) getString(R.string.voicemain_uri_help)
) )
} }
binding.TelephonyProviderTitle.setOnClickListener {
Utils.alertView(
this, getString(R.string.telephony_provider),
getString(R.string.telephony_provider_help)
)
}
binding.DefaultTitle.setOnClickListener { binding.DefaultTitle.setOnClickListener {
Utils.alertView( Utils.alertView(
this, getString(R.string.default_account), this, getString(R.string.default_account),
@@ -123,17 +123,14 @@ class ChatsActivity: AppCompatActivity() {
plusButton.setOnClickListener { plusButton.setOnClickListener {
val uriText = peerUri.text.toString().trim() val uriText = peerUri.text.toString().trim()
if (uriText.isNotEmpty()) { if (uriText.isNotEmpty()) {
var uri = ContactsActivity.findContactURI(uriText) val contactUri = ContactsActivity.findContactUri(uriText)
if (!uri.startsWith("sip:")) { val uri = if (contactUri == null)
uri = "sip:$uri" Utils.uriComplete(uriText, Utils.aorDomain(aor))
if (!uri.contains("@")) { else
val host = aor.substring(aor.indexOf("@") + 1) uriText
uri = "$uri@$host" if (!Utils.checkUri(uri)) {
}
}
if (!Utils.checkSipUri(uri)) {
Utils.alertView(this, getString(R.string.notice), Utils.alertView(this, getString(R.string.notice),
getString(R.string.invalid_chat_peer_uri)) String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
} else { } else {
peerUri.text.clear() peerUri.text.clear()
peerUri.isCursorVisible = false peerUri.isCursorVisible = false
@@ -33,6 +33,7 @@ import com.tutpro.baresip.databinding.ActivityContactBinding
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
class ContactActivity : AppCompatActivity() { class ContactActivity : AppCompatActivity() {
private lateinit var binding: ActivityContactBinding private lateinit var binding: ActivityContactBinding
@@ -83,12 +84,10 @@ class ContactActivity : AppCompatActivity() {
nameView.hint = getString(R.string.contact_name) nameView.hint = getString(R.string.contact_name)
nameView.setSelection(nameView.text.length) nameView.setSelection(nameView.text.length)
val uri = intent.getStringExtra("uri")!! val uri = intent.getStringExtra("uri")!!
if (uri == "") { if (uri == "")
uriView.setText("") uriView.setText("")
uriView.hint = getString(R.string.sip_uri) else
} else {
uriView.setText(uri) uriView.setText(uri)
}
uOrI = uri uOrI = uri
androidCheck.isChecked = false androidCheck.isChecked = false
} else { } else {
@@ -183,6 +182,9 @@ class ContactActivity : AppCompatActivity() {
requestPermissions(permissions, CONTACT_PERMISSION_REQUEST_CODE) requestPermissions(permissions, CONTACT_PERMISSION_REQUEST_CODE)
} }
Log.d(TAG, "********** Android sip contacts ${logAndroidContacts(this, "sip")}")
Log.d(TAG, "********** Android tel contacts ${logAndroidContacts(this, "tel")}")
Utils.addActivity("contact,$newContact,$uOrI") Utils.addActivity("contact,$newContact,$uOrI")
} }
@@ -255,7 +257,7 @@ class ContactActivity : AppCompatActivity() {
R.id.checkIcon -> { R.id.checkIcon -> {
var newName = nameView.text.toString().trim() var newName = nameView.text.toString().trim()
var newUri = uriView.text.toString().trim() var newUri = uriView.text.toString().filterNot{setOf('-', ' ').contains(it)}
if (newName == "") newName = newUri if (newName == "") newName = newUri
@@ -275,15 +277,14 @@ class ContactActivity : AppCompatActivity() {
return false return false
} }
if (!newUri.startsWith("sip:")) { if (!newUri.startsWith("sip:") && !newUri.startsWith("tel:"))
newUri = if (newUri.contains("@") || (BaresipService.uas.size != 1)) newUri = if (Utils.isTelNumber(newUri))
"sip:$newUri" "tel:$newUri"
else else
"sip:$newUri@${Utils.aorDomain(BaresipService.uas[0].account.aor)}" "sip:$newUri"
} if (!Utils.checkUri(newUri)) {
if (!Utils.checkSipUri(newUri)) {
Utils.alertView(this, getString(R.string.notice), Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_contact_uri), newUri)) String.format(getString(R.string.invalid_sip_or_tel_uri), newUri))
return false return false
} }
@@ -555,13 +556,16 @@ class ContactActivity : AppCompatActivity() {
} }
@Suppress("UNUSED") @Suppress("UNUSED")
fun logAndroidSipContacts(ctx: Context): HashMap<Long, MutableList<String>> { fun logAndroidContacts(ctx: Context, type: String): HashMap<Long, MutableList<String>> {
val mimeType = if (type == "sip")
CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
else
CommonDataKinds.Phone.CONTENT_ITEM_TYPE
val contacts = HashMap<Long, MutableList<String>>() val contacts = HashMap<Long, MutableList<String>>()
val projection = arrayOf(ContactsContract.Data.CONTACT_ID, ContactsContract.Data.DISPLAY_NAME, val projection = arrayOf(ContactsContract.Data.CONTACT_ID, ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1,
ContactsContract.Data.PHOTO_THUMBNAIL_URI) ContactsContract.Data.PHOTO_THUMBNAIL_URI)
val selection = ContactsContract.Data.MIMETYPE + "='" + val selection = ContactsContract.Data.MIMETYPE + "='" + mimeType + "'"
CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "'"
val cur: Cursor? = ctx.contentResolver.query(ContactsContract.Data.CONTENT_URI, projection, val cur: Cursor? = ctx.contentResolver.query(ContactsContract.Data.CONTENT_URI, projection,
selection, null, null) selection, null, null)
while (cur != null && cur.moveToNext()) { while (cur != null && cur.moveToNext()) {
@@ -574,6 +578,7 @@ class ContactActivity : AppCompatActivity() {
when (mime) { when (mime) {
CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> kind = "sip" CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> kind = "sip"
CommonDataKinds.Photo.CONTENT_ITEM_TYPE -> kind = "thumb" CommonDataKinds.Photo.CONTENT_ITEM_TYPE -> kind = "thumb"
CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> kind = "tel"
} }
Log.d(TAG, "got $id, $name, $kind - $data") Log.d(TAG, "got $id, $name, $kind - $data")
var info: MutableList<String> var info: MutableList<String>
@@ -11,10 +11,7 @@ import androidx.appcompat.app.AlertDialog
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ArrayAdapter import android.widget.*
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import java.io.File import java.io.File
@@ -79,13 +76,27 @@ class ContactListAdapter(private val ctx: Context, private val rows: ArrayList<C
i.putExtra("action", "call") i.putExtra("action", "call")
else else
i.putExtra("action", "message") i.putExtra("action", "message")
val ua = UserAgent.ofAor(aor) var ua = UserAgent.ofAor(aor)
if (ua == null) { if (ua == null) {
Log.w(TAG, "onClickListener did not find AoR $aor") Log.w(TAG, "onClickListener did not find AoR $aor")
} else {
if (Utils.isTelUri(contact.uri) && ua.account.telProvider == "") {
var newUa: UserAgent? = null
for (a in Account.accounts())
if (a.telProvider != "") {
newUa = UserAgent.ofAor(a.aor)
break
}
ua = newUa
}
}
if (ua == null) {
Toast.makeText(ctx, ctx.getString(R.string.no_telephony_providers),
Toast.LENGTH_LONG).show()
} else { } else {
BaresipService.activities.clear() BaresipService.activities.clear()
i.putExtra("uap", ua.uap) i.putExtra("uap", ua.uap)
i.putExtra("peer", Contact.contacts()[position].uri) i.putExtra("peer", contact.uri)
(ctx as Activity).startActivity(i) (ctx as Activity).startActivity(i)
} }
} }
@@ -89,19 +89,18 @@ class ContactsActivity : AppCompatActivity() {
companion object { companion object {
fun findContactURI(name: String): String { fun findContactUri(name: String): String? {
for (c in Contact.contacts()) for (c in Contact.contacts())
if (c.name == name) if (c.name.equals(name, ignoreCase = true))
return c.uri.removePrefix("<") return c.uri.removePrefix("<")
.replaceAfter(">", "") .replaceAfter(">", "")
.replace(">", "") .replace(">", "")
return name return null
} }
fun findContact(uri: String): Contact? { fun findContact(uri: String): Contact? {
for (c in Contact.contacts()) for (c in Contact.contacts())
if ((Utils.uriUserPart(c.uri) == Utils.uriUserPart(uri)) && if (Utils.uriMatch(c.uri, uri))
(Utils.uriHostPart(c.uri) == Utils.uriHostPart(uri)))
return c return c
return null return null
} }
@@ -112,17 +111,14 @@ class ContactsActivity : AppCompatActivity() {
return false return false
} }
// Return contact name of uri or uri itself if contact with uri is not found
fun contactName(uri: String): String { fun contactName(uri: String): String {
val uriUser = Utils.uriUserPart(uri) val userPart = Utils.uriUserPart(uri)
val uriHost = Utils.uriHostPart(uri) return if (Utils.isTelNumber(userPart))
for (c in Contact.contacts()) { findContact("tel:$userPart")?.name ?: uri
val contactUser = Utils.uriUserPart(c.uri) else
if ((contactUser == uriUser) && findContact(uri)?.name ?: uri
(Utils.isE164Number(contactUser) ||
(Utils.uriHostPart(c.uri) == uriHost)))
return c.name
}
return uri
} }
} }
} }
@@ -774,11 +774,19 @@ class MainActivity : AppCompatActivity() {
ua.account.resumeUri = uriStr ua.account.resumeUri = uriStr
} }
"tel" -> { "tel" -> {
val acc = BaresipService.uas[0].account var account: Account? = null
spinToAor(acc.aor) for (a in Account.accounts())
if (a.telProvider != "") {
account = a
break
}
if (account == null) {
Log.w(TAG, "No telephony providers for '$uriStr'")
return
}
spinToAor(account.aor)
resumeAction = "call" resumeAction = "call"
acc.resumeUri = uriStr.replace("tel", "sip") + "@" + account.resumeUri = uriStr
Utils.uriHostPart(acc.aor)
} }
else -> { else -> {
Log.w(TAG, "Unsupported URI scheme ${uri.scheme}") Log.w(TAG, "Unsupported URI scheme ${uri.scheme}")
@@ -1369,16 +1377,24 @@ class MainActivity : AppCompatActivity() {
setPositiveButton(R.string.transfer) { dialog, _ -> setPositiveButton(R.string.transfer) { dialog, _ ->
imm.hideSoftInputFromWindow(transferUri.windowToken, 0) imm.hideSoftInputFromWindow(transferUri.windowToken, 0)
dialog.dismiss() dialog.dismiss()
val uriText = transferUri.text.toString().trim() var uriText = transferUri.text.toString().trim()
if (uriText.isNotEmpty()) { if (uriText.isNotEmpty()) {
val uri = Utils.uriComplete( uriText = ContactsActivity.findContactUri(uriText) ?: uriText
ContactsActivity.findContactURI(uriText) if (Utils.isTelNumber(uriText))
.filterNot { it.isWhitespace() }, uriText = "tel:$uriText"
Utils.aorDomain(ua.account.aor) val uri = if (Utils.isTelUri(uriText)) {
) if (ua.account.telProvider == "") {
if (!Utils.checkSipUri(uri)) {
Utils.alertView(this@MainActivity, getString(R.string.notice), Utils.alertView(this@MainActivity, getString(R.string.notice),
String.format(getString(R.string.invalid_sip_uri), uri)) String.format(getString(R.string.no_telephony_provider)))
return@setPositiveButton
}
Utils.telToSip(uriText, ua.account)
} else {
Utils.uriComplete(uriText, Utils.aorDomain(ua.account.aor))
}
if (!Utils.checkUri(uri)) {
Utils.alertView(this@MainActivity, getString(R.string.notice),
String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
} else { } else {
if (Call.uaCalls(ua, "").size > 0) { if (Call.uaCalls(ua, "").size > 0) {
Call.uaCalls(ua, "")[0].transfer(uri) Call.uaCalls(ua, "")[0].transfer(uri)
@@ -1655,17 +1671,24 @@ class MainActivity : AppCompatActivity() {
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor val aor = ua.account.aor
if (Call.calls().isEmpty()) { if (Call.calls().isEmpty()) {
val uriText = callUri.text.toString().trim() var uriText = callUri.text.toString().trim()
if (uriText.isNotEmpty()) { if (uriText.isNotEmpty()) {
val uri = Utils.uriComplete( uriText = ContactsActivity.findContactUri(uriText) ?: uriText
ContactsActivity.findContactURI(uriText) if (Utils.isTelNumber(uriText))
.filterNot { it.isWhitespace() }, uriText = "tel:$uriText"
Utils.aorDomain(aor) val uri = if (Utils.isTelUri(uriText)) {
) if (ua.account.telProvider == "") {
if (!Utils.checkSipUri(uri)) { Utils.alertView(this, getString(R.string.notice),
Utils.alertView( String.format(getString(R.string.no_telephony_provider)))
this, getString(R.string.notice), return
String.format(getString(R.string.invalid_sip_uri), uri) }
Utils.telToSip(uriText, ua.account)
} else {
Utils.uriComplete(uriText, Utils.aorDomain(aor))
}
if (!Utils.checkUri(uri)) {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_sip_or_tel_uri), uri)
) )
} else { } else {
callUri.isFocusable = false callUri.isFocusable = false
@@ -105,6 +105,15 @@ object Utils {
"" ""
} }
fun uriMatch(firstUri: String, secondUri: String): Boolean {
if (firstUri.startsWith("tel:"))
return firstUri == secondUri
if (firstUri.startsWith("sip:"))
return uriUserPart(firstUri) == uriUserPart(secondUri) &&
uriHostPart(firstUri) == uriHostPart(secondUri)
return false
}
private fun uriParams(uri: String): List<String> { private fun uriParams(uri: String): List<String> {
val params = uri.split(";") val params = uri.split(";")
return if (params.size == 1) listOf() else params.subList(1, params.size) return if (params.size == 1) listOf() else params.subList(1, params.size)
@@ -121,7 +130,7 @@ object Utils {
return if (u.contains("@")) { return if (u.contains("@")) {
val user = uriUserPart(u) val user = uriUserPart(u)
val host = uriHostPart(u) val host = uriHostPart(u)
if (isE164Number(user) || host == domain) if (isTelNumber(user) || host == domain)
user user
else else
"$user@$host" "$user@$host"
@@ -164,10 +173,6 @@ object Utils {
checkTransport(uri.substringAfter("?"), setOf("udp", "tcp"))) checkTransport(uri.substringAfter("?"), setOf("udp", "tcp")))
} }
fun isE164Number(no: String): Boolean {
return Regex("^[+][1-9][0-9]{0,14}\$").matches(no)
}
fun checkIpV4(ip: String): Boolean { fun checkIpV4(ip: String): Boolean {
return Regex("^(([0-1]?[0-9]{1,2}\\.)|(2[0-4][0-9]\\.)|(25[0-5]\\.)){3}(([0-1]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))$").matches(ip) return Regex("^(([0-1]?[0-9]{1,2}\\.)|(2[0-4][0-9]\\.)|(25[0-5]\\.)){3}(([0-1]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))$").matches(ip)
} }
@@ -256,6 +261,12 @@ object Utils {
return "" return ""
} }
fun paramExists(params: String, name: String): Boolean {
for (param in params.split(";"))
if (param.substringBefore("=") == name) return true
return false
}
fun checkHostPortParams(hpp: String) : Boolean { fun checkHostPortParams(hpp: String) : Boolean {
val restParams = hpp.split(";", limit = 2) val restParams = hpp.split(";", limit = 2)
return if (restParams.size == 1) return if (restParams.size == 1)
@@ -264,16 +275,40 @@ object Utils {
checkHostPort(restParams[0]) && checkParams(restParams[1]) checkHostPort(restParams[0]) && checkParams(restParams[1])
} }
fun checkSipUri(uri: String): Boolean { private fun checkSipUri(uri: String): Boolean {
if (!uri.startsWith("sip:")) return false return if (uri.startsWith("sip:")) {
val userRest = uri.substring(4).split("@") val userRest = uri.substring(4).split("@")
return when (userRest.size) { when (userRest.size) {
1 -> 1 ->
checkHostPortParams(userRest[0]) checkHostPortParams(userRest[0])
2 -> 2 ->
checkUriUser(userRest[0]) && checkHostPortParams(userRest[1]) checkUriUser(userRest[0]) && checkHostPortParams(userRest[1])
else -> false else -> false
} }
} else {
false
}
}
fun isTelNumber(no: String): Boolean {
return Regex("^[+][1-9][0-9- ]{0,14}\$").matches(no)
}
fun isTelUri(uri: String): Boolean {
return uri.startsWith("tel:") && isTelNumber(uri.substring(4))
}
fun checkUri(uri: String): Boolean {
return checkSipUri(uri) || isTelUri(uri)
}
fun telToSip(telUri: String, account: Account): String {
val hostPart = if (account.telProvider != "")
account.telProvider
else
aorDomain(account.aor)
return "sip:" + telUri.substring(4).filterNot{setOf('-', ' ').contains(it)} +
"@" + hostPart + ";user=phone"
} }
fun checkName(name: String): Boolean { fun checkName(name: String): Boolean {
@@ -178,6 +178,12 @@
android:paddingTop="8dp" > android:paddingTop="8dp" >
</Spinner> </Spinner>
<LinearLayout
android:id="@+id/Stun"
android:layout_width="fill_parent"
android:orientation="vertical"
android:layout_height="wrap_content" >
<TextView <TextView
android:id="@+id/StunServerTitle" android:id="@+id/StunServerTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -242,6 +248,8 @@
</androidx.appcompat.widget.AppCompatEditText> </androidx.appcompat.widget.AppCompatEditText>
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<TextView <TextView
android:id="@+id/DtmfModeTitle" android:id="@+id/DtmfModeTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -293,6 +301,25 @@
android:layout_width="fill_parent"> android:layout_width="fill_parent">
</EditText> </EditText>
<TextView
android:id="@+id/TelephonyProviderTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:text="@string/telephony_provider" >
</TextView>
<EditText
android:id="@+id/telephonyProvider"
android:layout_height="wrap_content"
android:hint="@string/telephony_provider_hint"
android:textSize="18sp"
android:inputType="textUri"
android:importantForAutofill="no"
android:layout_width="fill_parent">
</EditText>
<RelativeLayout <RelativeLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
+3 -3
View File
@@ -63,19 +63,19 @@
</EditText> </EditText>
<TextView <TextView
android:id="@+id/SipUriTitle" android:id="@+id/SipOrTelUriTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="18sp" android:textSize="18sp"
android:paddingTop="10dp" android:paddingTop="10dp"
android:text="@string/sip_uri" > android:text="@string/sip_or_tel_uri" >
</TextView> </TextView>
<EditText <EditText
android:id="@+id/Uri" android:id="@+id/Uri"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/sip_uri" android:hint="@string/user_domain_or_number"
android:inputType="textEmailAddress" android:inputType="textEmailAddress"
android:textSize="18sp" android:textSize="18sp"
android:importantForAutofill="no" android:importantForAutofill="no"
+13 -1
View File
@@ -182,6 +182,13 @@
messages (Message Waiting Indications) are not subscribed to. messages (Message Waiting Indications) are not subscribed to.
</string> </string>
<string name="invalid_voicemail_uri">Invalid Voicemail URI \'%1$s\'</string> <string name="invalid_voicemail_uri">Invalid Voicemail URI \'%1$s\'</string>
<string name="telephony_provider">Telephony Provider</string>
<string name="telephony_provider_help">SIP URI host part used in calls to telephone numbers.
Factory default is account\'s domain. If not given, this account cannot be used to call
telephone numbers.
</string>
<string name="telephony_provider_hint">SIP URI host part</string>
<string name="invalid_sip_uri_hostpart">Invalid SIP URI host part \'%1$s\'</string>
<string name="default_account">Default Account</string> <string name="default_account">Default Account</string>
<string name="default_account_help">If checked, this account is selected when baresip is started. <string name="default_account_help">If checked, this account is selected when baresip is started.
</string> </string>
@@ -247,7 +254,7 @@
<string name="today">Today</string> <string name="today">Today</string>
<string name="you">You</string> <string name="you">You</string>
<string name="new_chat_peer">New Chat Peer</string> <string name="new_chat_peer">New Chat Peer</string>
<string name="invalid_chat_peer_uri">Invalid SIP URI</string> <string name="invalid_chat_peer_uri">Invalid chat peer URI</string>
<string name="long_chat_question">Do you want to delete chat with peer \'%1$s\' or <string name="long_chat_question">Do you want to delete chat with peer \'%1$s\' or
add peer to contacts?</string> add peer to contacts?</string>
<string name="short_chat_question">Do you want to delete chat with \'%1$s\'?</string> <string name="short_chat_question">Do you want to delete chat with \'%1$s\'?</string>
@@ -341,6 +348,8 @@
<string name="contact">Contact</string> <string name="contact">Contact</string>
<string name="new_contact">New Contact</string> <string name="new_contact">New Contact</string>
<string name="contact_name">Name</string> <string name="contact_name">Name</string>
<string name="sip_or_tel_uri">SIP or tel URI</string>
<string name="user_domain_or_number">user@domain or telephone number</string>
<string name="invalid_contact">Invalid contact name \'%1$s\'</string> <string name="invalid_contact">Invalid contact name \'%1$s\'</string>
<string name="contact_already_exists">Contact \'%1$s\' already exists.</string> <string name="contact_already_exists">Contact \'%1$s\' already exists.</string>
<string name="invalid_contact_uri">Invalid SIP URI</string> <string name="invalid_contact_uri">Invalid SIP URI</string>
@@ -353,6 +362,7 @@
<string name="send_message">Send Message</string> <string name="send_message">Send Message</string>
<string name="contact_delete_question">Do you want to delete contact \'%1$s\'?</string> <string name="contact_delete_question">Do you want to delete contact \'%1$s\'?</string>
<string name="contacts_exceeded">Your maximum number of contacts %1$d has been exceeded.</string> <string name="contacts_exceeded">Your maximum number of contacts %1$d has been exceeded.</string>
<string name="no_telephony_providers">Telephony provider has not been configured for any account.</string>
<!-- Generic --> <!-- Generic -->
<string name="alert">Alert</string> <string name="alert">Alert</string>
<string name="info">Info</string> <string name="info">Info</string>
@@ -376,6 +386,7 @@
<string name="confirmation">Confirmation</string> <string name="confirmation">Confirmation</string>
<string name="dots" translatable="false"></string> <string name="dots" translatable="false"></string>
<string name="bullet_item" translatable="false">\u2022 %1$s</string> <string name="bullet_item" translatable="false">\u2022 %1$s</string>
<string name="invalid_sip_or_tel_uri">Invalid SIP or tel URI \'%1$s\'</string>
<!-- Main Activity --> <!-- Main Activity -->
<string name="backup">Backup</string> <string name="backup">Backup</string>
<string name="restore">Restore</string> <string name="restore">Restore</string>
@@ -386,6 +397,7 @@
<string name="incoming_call_from_dots">Call from …</string> <string name="incoming_call_from_dots">Call from …</string>
<string name="transferring_call_to_dots">Transferring call to …</string> <string name="transferring_call_to_dots">Transferring call to …</string>
<string name="invalid_sip_uri">Invalid SIP URI \'%1$s\'</string> <string name="invalid_sip_uri">Invalid SIP URI \'%1$s\'</string>
<string name="no_telephony_provider">Telephony provider has not been configured for this acccount.</string>
<string name="callee">Callee</string> <string name="callee">Callee</string>
<string name="hangup">Hangup</string> <string name="hangup">Hangup</string>
<string name="video_call">Video call</string> <string name="video_call">Video call</string>