Merge branch 'tel_uri'

This commit is contained in:
Juha Heinanen
2022-02-03 12:01:35 +02:00
11 changed files with 322 additions and 168 deletions

View File

@ -3,6 +3,8 @@ package com.tutpro.baresip
import android.content.Context
import java.util.*
import kotlin.collections.ArrayList
import java.net.URLEncoder
import java.net.URLDecoder
class Account(val accp: String) {
@ -28,6 +30,7 @@ class Account(val accp: String) {
var missedCalls = false
var unreadMessages = false
var callHistory = true
var telProvider = Utils.aorDomain(aor)
var resumeUri = ""
init {
@ -56,6 +59,10 @@ class Account(val accp: String) {
val extra = Api.account_extra(accp)
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)
extra += ";call_history=no"
extra += ";tel_provider=${URLEncoder.encode(telProvider, "UTF-8")}"
if (extra != "")
res += ";extra=\"" + extra.substringAfter(";") + "\""
@ -189,6 +198,14 @@ class Account(val accp: String) {
return res
}
fun telProviderAccounts(): Array<String> {
val res = ArrayList<String>()
for (account in accounts())
if (account.telProvider != "")
res.add(account.aor.substring(4))
return res.toTypedArray()
}
fun ofAor(aor: String): Account? {
for (ua in UserAgent.uas())
if (ua.account.aor == aor) return ua.account

View File

@ -2,10 +2,13 @@ package com.tutpro.baresip
import android.app.Activity
import android.content.Intent
import android.opengl.Visibility
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import com.tutpro.baresip.databinding.ActivityAccountBinding
@ -33,6 +36,7 @@ class AccountActivity : AppCompatActivity() {
private lateinit var outbound1: EditText
private lateinit var outbound2: EditText
private lateinit var mediaNat: String
private lateinit var stun: LinearLayout
private lateinit var stunServer: EditText
private lateinit var stunUser: EditText
private lateinit var stunPass: EditText
@ -45,6 +49,7 @@ class AccountActivity : AppCompatActivity() {
private var answerMode = Api.ANSWERMODE_MANUAL
private lateinit var answerModeSpinner: Spinner
private lateinit var vmUri: EditText
private lateinit var telProvider: EditText
private lateinit var defaultCheck: CheckBox
private val mediaEncKeys = arrayListOf("zrtp", "dtls_srtp", "srtp-mandf", "srtp-mand", "srtp", "")
@ -73,6 +78,7 @@ class AccountActivity : AppCompatActivity() {
outbound2 = binding.Outbound2
regCheck = binding.Register
mediaNatSpinner = binding.mediaNatSpinner
stun = binding.Stun
stunServer = binding.StunServer
stunUser = binding.StunUser
stunPass = binding.StunPass
@ -80,6 +86,7 @@ class AccountActivity : AppCompatActivity() {
dtmfModeSpinner = binding.dtmfModeSpinner
answerModeSpinner = binding.answerModeSpinner
vmUri = binding.voicemailUri
telProvider = binding.telephonyProvider
defaultCheck = binding.Default
aor = intent.getStringExtra("aor")!!
@ -230,27 +237,21 @@ class AccountActivity : AppCompatActivity() {
stunServer.setText(resources.getString(R.string.stun_server_default))
else if ((mediaNat == "ice") && (stunServer.text.toString() == ""))
stunServer.setText(resources.getString(R.string.stun_server_default))
else if (mediaNat == "") {
if (mediaNat == "") {
stun.visibility = GONE
stunServer.setText("")
stunUser.setText("")
stunPass.setText("")
}
stunServer.isEnabled = mediaNat != ""
stunUser.isEnabled = mediaNat != ""
stunPass.isEnabled = mediaNat != ""
} else
stun.visibility = VISIBLE
}
override fun onNothingSelected(parent: AdapterView<*>) {
}
}
stunServer.setText(acc.stunServer)
stunServer.isEnabled = mediaNat != ""
stunUser.setText(acc.stunUser)
stunUser.isEnabled = mediaNat != ""
stunPass.setText(acc.stunPass)
stunPass.isEnabled = mediaNat != ""
mediaEnc = acc.mediaEnc
keyIx = mediaEncKeys.indexOf(mediaEnc)
@ -313,6 +314,8 @@ class AccountActivity : AppCompatActivity() {
}
}
telProvider.setText(acc.telProvider)
vmUri.setText(acc.vmUri)
defaultCheck.isChecked = uaIndex == 0
@ -563,9 +566,9 @@ class AccountActivity : AppCompatActivity() {
if (tVmUri != "") {
if (!tVmUri.startsWith("sip:")) tVmUri = "sip:$tVmUri"
if (!tVmUri.contains("@")) tVmUri = "$tVmUri@${acc.host()}"
if (!Utils.checkSipUri(tVmUri)) {
if (!Utils.checkUri(tVmUri)) {
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
}
Api.account_set_mwi(acc.accp, "yes")
@ -576,6 +579,17 @@ class AccountActivity : AppCompatActivity() {
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)) {
val uasTmp = BaresipService.uas[0]
val statusTmp = BaresipService.status[0]
@ -708,6 +722,12 @@ class AccountActivity : AppCompatActivity() {
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 {
Utils.alertView(
this, getString(R.string.default_account),

View File

@ -123,17 +123,14 @@ class ChatsActivity: AppCompatActivity() {
plusButton.setOnClickListener {
val uriText = peerUri.text.toString().trim()
if (uriText.isNotEmpty()) {
var uri = ContactsActivity.findContactURI(uriText)
if (!uri.startsWith("sip:")) {
uri = "sip:$uri"
if (!uri.contains("@")) {
val host = aor.substring(aor.indexOf("@") + 1)
uri = "$uri@$host"
}
}
if (!Utils.checkSipUri(uri)) {
val contactUri = ContactsActivity.findContactUri(uriText)
val uri = if (contactUri == null)
Utils.uriComplete(uriText, Utils.aorDomain(aor))
else
uriText
if (!Utils.checkUri(uri)) {
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 {
peerUri.text.clear()
peerUri.isCursorVisible = false

View File

@ -33,6 +33,7 @@ import com.tutpro.baresip.databinding.ActivityContactBinding
import java.io.ByteArrayOutputStream
import java.io.File
class ContactActivity : AppCompatActivity() {
private lateinit var binding: ActivityContactBinding
@ -83,12 +84,10 @@ class ContactActivity : AppCompatActivity() {
nameView.hint = getString(R.string.contact_name)
nameView.setSelection(nameView.text.length)
val uri = intent.getStringExtra("uri")!!
if (uri == "") {
if (uri == "")
uriView.setText("")
uriView.hint = getString(R.string.sip_uri)
} else {
else
uriView.setText(uri)
}
uOrI = uri
androidCheck.isChecked = false
} else {
@ -183,6 +182,9 @@ class ContactActivity : AppCompatActivity() {
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")
}
@ -255,7 +257,7 @@ class ContactActivity : AppCompatActivity() {
R.id.checkIcon -> {
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
@ -275,15 +277,14 @@ class ContactActivity : AppCompatActivity() {
return false
}
if (!newUri.startsWith("sip:")) {
newUri = if (newUri.contains("@") || (BaresipService.uas.size != 1))
"sip:$newUri"
if (!newUri.startsWith("sip:") && !newUri.startsWith("tel:"))
newUri = if (Utils.isTelNumber(newUri))
"tel:$newUri"
else
"sip:$newUri@${Utils.aorDomain(BaresipService.uas[0].account.aor)}"
}
if (!Utils.checkSipUri(newUri)) {
"sip:$newUri"
if (!Utils.checkUri(newUri)) {
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
}
@ -433,10 +434,14 @@ class ContactActivity : AppCompatActivity() {
.withValue(Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(CommonDataKinds.StructuredName.DISPLAY_NAME, contact.name)
.build())
val mimeType = if (contact.uri.startsWith("sip:"))
CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
else
CommonDataKinds.Phone.CONTENT_ITEM_TYPE
ops.add(ContentProviderOperation
.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
.withValue(Data.MIMETYPE, CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(Data.MIMETYPE, mimeType)
.withValue(Data.DATA1, contact.uri.substringAfter(":"))
.build())
if (contact.avatarImage != null) {
@ -460,37 +465,45 @@ class ContactActivity : AppCompatActivity() {
}
private fun updateAndroidContact(rawContactId: Long, contact: Contact) {
if (updateAndroidSipUri(rawContactId, contact.uri) == 0)
addAndroidSipUri(rawContactId, contact.uri)
if (updateAndroidUri(rawContactId, contact.uri) == 0)
addAndroidUri(rawContactId, contact.uri)
if (updateAndroidPhoto(rawContactId, contact.avatarImage) == 0)
if (contact.avatarImage != null)
addAndroidPhoto(rawContactId, contact.avatarImage!!)
}
private fun addAndroidSipUri(rawContactId: Long, sipUri: String) {
private fun addAndroidUri(rawContactId: Long, uri: String) {
val mimeType = if (uri.startsWith("sip:"))
CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
else
CommonDataKinds.Phone.CONTENT_ITEM_TYPE
val ops = ArrayList<ContentProviderOperation>()
ops.add(ContentProviderOperation
.newInsert(ContactsContract.Data.CONTENT_URI)
.withValue(Data.RAW_CONTACT_ID, rawContactId)
.withValue(Data.MIMETYPE, CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE)
.withValue(Data.DATA1, sipUri.substringAfter(":"))
.withValue(Data.MIMETYPE, mimeType)
.withValue(Data.DATA1, uri.substringAfter(":"))
.build())
try {
contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
} catch (e: Exception) {
Log.e(TAG, "Adding of SIP URI $sipUri failed")
Log.e(TAG, "Adding of SIP URI $uri failed")
}
}
private fun updateAndroidSipUri(rawContactId: Long, sipUri: String): Int {
private fun updateAndroidUri(rawContactId: Long, uri: String): Int {
val mimeType = if (uri.startsWith("sip:"))
CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE
else
CommonDataKinds.Phone.CONTENT_ITEM_TYPE
val contentValues = ContentValues()
contentValues.put(ContactsContract.Data.DATA1, sipUri)
contentValues.put(ContactsContract.Data.DATA1, uri)
val where = "${ContactsContract.Data.RAW_CONTACT_ID}=$rawContactId and " +
"${ContactsContract.Data.MIMETYPE}='${CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE}'"
"${ContactsContract.Data.MIMETYPE}='$mimeType'"
return try {
contentResolver.update(ContactsContract.Data.CONTENT_URI, contentValues, where, null)
} catch (e: Exception) {
Log.e(TAG, "Adding of SIP URI $sipUri failed")
Log.e(TAG, "Adding of SIP URI $uri failed")
0
}
}
@ -555,13 +568,16 @@ class ContactActivity : AppCompatActivity() {
}
@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 projection = arrayOf(ContactsContract.Data.CONTACT_ID, ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1,
ContactsContract.Data.PHOTO_THUMBNAIL_URI)
val selection = ContactsContract.Data.MIMETYPE + "='" +
CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "'"
val selection = ContactsContract.Data.MIMETYPE + "='" + mimeType + "'"
val cur: Cursor? = ctx.contentResolver.query(ContactsContract.Data.CONTENT_URI, projection,
selection, null, null)
while (cur != null && cur.moveToNext()) {
@ -574,6 +590,7 @@ class ContactActivity : AppCompatActivity() {
when (mime) {
CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> kind = "sip"
CommonDataKinds.Photo.CONTENT_ITEM_TYPE -> kind = "thumb"
CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> kind = "tel"
}
Log.d(TAG, "got $id, $name, $kind - $data")
var info: MutableList<String>

View File

@ -11,10 +11,7 @@ import androidx.appcompat.app.AlertDialog
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
import android.widget.*
import androidx.core.content.ContextCompat.startActivity
import java.io.File
@ -85,7 +82,7 @@ class ContactListAdapter(private val ctx: Context, private val rows: ArrayList<C
} else {
BaresipService.activities.clear()
i.putExtra("uap", ua.uap)
i.putExtra("peer", Contact.contacts()[position].uri)
i.putExtra("peer", contact.uri)
(ctx as Activity).startActivity(i)
}
}

View File

@ -89,19 +89,18 @@ class ContactsActivity : AppCompatActivity() {
companion object {
fun findContactURI(name: String): String {
fun findContactUri(name: String): String? {
for (c in Contact.contacts())
if (c.name == name)
if (c.name.equals(name, ignoreCase = true))
return c.uri.removePrefix("<")
.replaceAfter(">", "")
.replace(">", "")
return name
return null
}
fun findContact(uri: String): Contact? {
for (c in Contact.contacts())
if ((Utils.uriUserPart(c.uri) == Utils.uriUserPart(uri)) &&
(Utils.uriHostPart(c.uri) == Utils.uriHostPart(uri)))
if (Utils.uriMatch(c.uri, uri))
return c
return null
}
@ -112,17 +111,14 @@ class ContactsActivity : AppCompatActivity() {
return false
}
// Return contact name of uri or uri itself if contact with uri is not found
fun contactName(uri: String): String {
val uriUser = Utils.uriUserPart(uri)
val uriHost = Utils.uriHostPart(uri)
for (c in Contact.contacts()) {
val contactUser = Utils.uriUserPart(c.uri)
if ((contactUser == uriUser) &&
(Utils.isE164Number(contactUser) ||
(Utils.uriHostPart(c.uri) == uriHost)))
return c.name
}
return uri
val userPart = Utils.uriUserPart(uri)
return if (Utils.isTelNumber(userPart))
findContact("tel:$userPart")?.name ?: uri
else
findContact(uri)?.name ?: uri
}
}
}

View File

@ -763,22 +763,36 @@ class MainActivity : AppCompatActivity() {
return
val uri: Uri? = intent.data
if (uri != null) {
val uriStr = URLDecoder.decode(uri.toString(), "UTF-8").replace(" ", "")
when (uri.scheme) {
"sip" -> {
val uriStr = URLDecoder.decode(uri.toString(), "UTF-8")
var ua = UserAgent.ofDomain(Utils.uriHostPart(uriStr))
if (ua == null)
if (ua == null && BaresipService.uas.size > 0)
ua = BaresipService.uas[0]
if (ua == null) {
Log.w(TAG, "No accounts for '$uriStr'")
return
}
spinToAor(ua.account.aor)
resumeAction = "call"
ua.account.resumeUri = uriStr
}
"tel" -> {
val acc = BaresipService.uas[0].account
spinToAor(acc.aor)
val uriStr = URLDecoder.decode(uri.toString(), "UTF-8")
.filterNot{setOf('-', ' ').contains(it)}
var account: Account? = null
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"
acc.resumeUri = uriStr.replace("tel", "sip") + "@" +
Utils.uriHostPart(acc.aor)
account.resumeUri = uriStr
}
else -> {
Log.w(TAG, "Unsupported URI scheme ${uri.scheme}")
@ -1369,16 +1383,18 @@ class MainActivity : AppCompatActivity() {
setPositiveButton(R.string.transfer) { dialog, _ ->
imm.hideSoftInputFromWindow(transferUri.windowToken, 0)
dialog.dismiss()
val uriText = transferUri.text.toString().trim()
var uriText = transferUri.text.toString().trim()
if (uriText.isNotEmpty()) {
val uri = Utils.uriComplete(
ContactsActivity.findContactURI(uriText)
.filterNot { it.isWhitespace() },
Utils.aorDomain(ua.account.aor)
)
if (!Utils.checkSipUri(uri)) {
uriText = ContactsActivity.findContactUri(uriText) ?: uriText
if (Utils.isTelNumber(uriText))
uriText = "tel:$uriText"
val uri = if (Utils.isTelUri(uriText))
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_uri), uri))
String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
} else {
if (Call.uaCalls(ua, "").size > 0) {
Call.uaCalls(ua, "")[0].transfer(uri)
@ -1655,17 +1671,38 @@ class MainActivity : AppCompatActivity() {
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor
if (Call.calls().isEmpty()) {
val uriText = callUri.text.toString().trim()
var uriText = callUri.text.toString().trim()
if (uriText.isNotEmpty()) {
val uri = Utils.uriComplete(
ContactsActivity.findContactURI(uriText)
.filterNot { it.isWhitespace() },
Utils.aorDomain(aor)
)
if (!Utils.checkSipUri(uri)) {
Utils.alertView(
this, getString(R.string.notice),
String.format(getString(R.string.invalid_sip_uri), uri)
uriText = ContactsActivity.findContactUri(uriText) ?: uriText
if (Utils.isTelNumber(uriText))
uriText = "tel:$uriText"
val uri = if (Utils.isTelUri(uriText)) {
if (ua.account.telProvider == "") {
val telAccounts = Account.telProviderAccounts()
if (telAccounts.isEmpty()) {
Utils.alertView(this, getString(R.string.notice),
getString(R.string.no_telephony_providers))
} else {
val builder = AlertDialog.Builder(this)
with(builder) {
setTitle(getString(R.string.choose_telephony_provider_account))
setItems(telAccounts) { _, which ->
spinToAor("sip:${telAccounts[which]}")
makeCall()
}
setPositiveButton("Cancel") { _: DialogInterface, _: Int -> }
show()
}
}
return
}
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 {
callUri.isFocusable = false
@ -1686,8 +1723,7 @@ class MainActivity : AppCompatActivity() {
if (latest != null)
callUri.setText(
Utils.friendlyUri(
ContactsActivity.contactName(latest.peerUri),
Utils.aorDomain(ua.account.aor)
ContactsActivity.contactName(latest.peerUri), Utils.aorDomain(aor)
)
)
}

View File

@ -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> {
val params = uri.split(";")
return if (params.size == 1) listOf() else params.subList(1, params.size)
@ -121,7 +130,7 @@ object Utils {
return if (u.contains("@")) {
val user = uriUserPart(u)
val host = uriHostPart(u)
if (isE164Number(user) || host == domain)
if (isTelNumber(user) || host == domain)
user
else
"$user@$host"
@ -164,10 +173,6 @@ object Utils {
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 {
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 ""
}
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 {
val restParams = hpp.split(";", limit = 2)
return if (restParams.size == 1)
@ -264,18 +275,42 @@ object Utils {
checkHostPort(restParams[0]) && checkParams(restParams[1])
}
fun checkSipUri(uri: String): Boolean {
if (!uri.startsWith("sip:")) return false
val userRest = uri.substring(4).split("@")
return when (userRest.size) {
1 ->
checkHostPortParams(userRest[0])
2 ->
checkUriUser(userRest[0]) && checkHostPortParams(userRest[1])
else -> false
private fun checkSipUri(uri: String): Boolean {
return if (uri.startsWith("sip:")) {
val userRest = uri.substring(4).split("@")
when (userRest.size) {
1 ->
checkHostPortParams(userRest[0])
2 ->
checkUriUser(userRest[0]) && checkHostPortParams(userRest[1])
else -> false
}
} else {
false
}
}
fun isTelNumber(no: String): Boolean {
return Regex("^([+][1-9])?[0-9- ]{0,15}\$").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 {
return name.isNotEmpty() && name == String(name.toByteArray(), Charsets.UTF_8) &&
name.lines().size == 1 && !name.contains('"')

View File

@ -178,69 +178,77 @@
android:paddingTop="8dp" >
</Spinner>
<TextView
android:id="@+id/StunServerTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:text="@string/stun_server" >
</TextView>
<EditText
android:id="@+id/StunServer"
android:layout_height="wrap_content"
<LinearLayout
android:id="@+id/Stun"
android:layout_width="fill_parent"
android:textSize="18sp"
android:scrollHorizontally="true"
android:importantForAutofill="no"
android:hint="@string/stun_server_uri"
android:inputType="textEmailAddress" >
</EditText>
android:orientation="vertical"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/StunUserTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:text="@string/stun_username" >
</TextView>
<TextView
android:id="@+id/StunServerTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:text="@string/stun_server" >
</TextView>
<EditText
android:id="@+id/StunUser"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:hint="@string/user_id"
android:textSize="18sp"
android:inputType="textEmailAddress"
android:autofillHints="username" >
</EditText>
<EditText
android:id="@+id/StunServer"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:textSize="18sp"
android:scrollHorizontally="true"
android:importantForAutofill="no"
android:hint="@string/stun_server_uri"
android:inputType="textEmailAddress" >
</EditText>
<TextView
android:id="@+id/StunPassTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:text="@string/stun_password" >
</TextView>
<TextView
android:id="@+id/StunUserTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:text="@string/stun_username" >
</TextView>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:hintEnabled="false"
app:endIconMode="password_toggle">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/StunPass"
<EditText
android:id="@+id/StunUser"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:hint="@string/user_id"
android:textSize="18sp"
android:inputType="textEmailAddress"
android:autofillHints="username" >
</EditText>
<TextView
android:id="@+id/StunPassTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:text="@string/stun_password" >
</TextView>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="flagNoExtractUi"
android:inputType="textPassword"
android:hint="@string/password"
android:maxLines="1">
</androidx.appcompat.widget.AppCompatEditText>
</com.google.android.material.textfield.TextInputLayout>
app:hintEnabled="false"
app:endIconMode="password_toggle">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/StunPass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="flagNoExtractUi"
android:inputType="textPassword"
android:hint="@string/password"
android:maxLines="1">
</androidx.appcompat.widget.AppCompatEditText>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<TextView
android:id="@+id/DtmfModeTitle"
@ -293,6 +301,25 @@
android:layout_width="fill_parent">
</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
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@ -63,19 +63,19 @@
</EditText>
<TextView
android:id="@+id/SipUriTitle"
android:id="@+id/SipOrTelUriTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:paddingTop="10dp"
android:text="@string/sip_uri" >
android:text="@string/sip_or_tel_uri" >
</TextView>
<EditText
android:id="@+id/Uri"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/sip_uri"
android:hint="@string/user_domain_or_number"
android:inputType="textEmailAddress"
android:textSize="18sp"
android:importantForAutofill="no"

View File

@ -182,6 +182,13 @@
messages (Message Waiting Indications) are not subscribed to.
</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_help">If checked, this account is selected when baresip is started.
</string>
@ -247,7 +254,7 @@
<string name="today">Today</string>
<string name="you">You</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
add peer to contacts?</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="new_contact">New Contact</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="contact_already_exists">Contact \'%1$s\' already exists.</string>
<string name="invalid_contact_uri">Invalid SIP URI</string>
@ -353,6 +362,7 @@
<string name="send_message">Send Message</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="no_telephony_providers">Telephony Provider has not been configured for any account.</string>
<!-- Generic -->
<string name="alert">Alert</string>
<string name="info">Info</string>
@ -376,6 +386,7 @@
<string name="confirmation">Confirmation</string>
<string name="dots" translatable="false"></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 -->
<string name="backup">Backup</string>
<string name="restore">Restore</string>
@ -386,6 +397,7 @@
<string name="incoming_call_from_dots">Call from …</string>
<string name="transferring_call_to_dots">Transferring call to …</string>
<string name="invalid_sip_uri">Invalid SIP URI \'%1$s\'</string>
<string name="choose_telephony_provider_account">Choose Telephony Provider account.</string>
<string name="callee">Callee</string>
<string name="hangup">Hangup</string>
<string name="video_call">Video call</string>