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

View File

@ -362,8 +362,8 @@ static struct mqueue *mq;
static void mqueue_handler(int id, void *data, void *arg)
{
if (id == ID_UA_STOP_ALL) {
LOGD("calling ua_stop_all with force %u\n", (unsigned)data);
ua_stop_all((bool)data);
LOGD("calling ua_stop_all with force %u\n", (unsigned)(uintptr_t)data);
ua_stop_all((bool)(uintptr_t)data);
}
}

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(";") + "\""

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
}
@ -555,13 +556,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 +578,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
@ -79,13 +76,27 @@ class ContactListAdapter(private val ctx: Context, private val rows: ArrayList<C
i.putExtra("action", "call")
else
i.putExtra("action", "message")
val ua = UserAgent.ofAor(aor)
var ua = UserAgent.ofAor(aor)
if (ua == null) {
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 {
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

@ -774,11 +774,19 @@ class MainActivity : AppCompatActivity() {
ua.account.resumeUri = uriStr
}
"tel" -> {
val acc = BaresipService.uas[0].account
spinToAor(acc.aor)
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 +1377,24 @@ 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)) {
if (ua.account.telProvider == "") {
Utils.alertView(this@MainActivity, getString(R.string.notice),
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_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,24 @@ 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 == "") {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.no_telephony_provider)))
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

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,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 {
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="no_telephony_provider">Telephony provider has not been configured for this acccount.</string>
<string name="callee">Callee</string>
<string name="hangup">Hangup</string>
<string name="video_call">Video call</string>