Merge branch 'tel_uri'
This commit is contained in:
@ -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
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -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('"')
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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>
|
||||
|
||||
Reference in New Issue
Block a user