Added Country Code account configuration option

This commit is contained in:
Juha Heinanen
2022-04-10 17:10:55 +03:00
parent ba700b010e
commit 39c8563d56
14 changed files with 143 additions and 33 deletions

View File

@ -30,6 +30,7 @@ class Account(val accp: Long) {
var missedCalls = false
var unreadMessages = false
var callHistory = true
var countryCode = ""
var telProvider = Utils.aorDomain(aor)
var resumeUri = ""
@ -59,6 +60,10 @@ class Account(val accp: Long) {
val extra = Api.account_extra(accp)
callHistory = Utils.paramValue(extra,"call_history") == ""
countryCode = if (Utils.paramExists(extra, "country_code"))
Utils.paramValue(extra,"country_code")
else
""
telProvider = if (Utils.paramExists(extra, "tel_provider"))
URLDecoder.decode(Utils.paramValue(extra,"tel_provider"), "UTF-8")
else
@ -128,6 +133,9 @@ class Account(val accp: Long) {
extra += ";tel_provider=${URLEncoder.encode(telProvider, "UTF-8")}"
if (countryCode != "")
extra += ";country_code=$countryCode"
if (extra != "")
res += ";extra=\"" + extra.substringAfter(";") + "\""

View File

@ -47,6 +47,7 @@ class AccountActivity : AppCompatActivity() {
private var answerMode = Api.ANSWERMODE_MANUAL
private lateinit var answerModeSpinner: Spinner
private lateinit var vmUri: EditText
private lateinit var countryCode: EditText
private lateinit var telProvider: EditText
private lateinit var defaultCheck: CheckBox
@ -84,6 +85,7 @@ class AccountActivity : AppCompatActivity() {
dtmfModeSpinner = binding.dtmfModeSpinner
answerModeSpinner = binding.answerModeSpinner
vmUri = binding.voicemailUri
countryCode = binding.countryCode
telProvider = binding.telephonyProvider
defaultCheck = binding.Default
@ -182,6 +184,8 @@ class AccountActivity : AppCompatActivity() {
"voicemail-uri" ->
if (text.isNotEmpty())
acc.vmUri = text
"country-code" ->
acc.countryCode = text
"tel-provider" ->
acc.telProvider = text
}
@ -314,6 +318,9 @@ class AccountActivity : AppCompatActivity() {
}
}
if (acc.countryCode != "")
countryCode.setText(acc.countryCode)
telProvider.setText(acc.telProvider)
vmUri.setText(acc.vmUri)
@ -579,6 +586,17 @@ class AccountActivity : AppCompatActivity() {
save = true
}
val newCountryCode = countryCode.text.toString().trim()
if (newCountryCode != acc.countryCode) {
if (newCountryCode != "" && !Utils.checkCountryCode(newCountryCode)) {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_country_code), newCountryCode))
return false
}
acc.countryCode = newCountryCode
save = true
}
val hostPart = telProvider.text.toString().trim()
if (hostPart != acc.telProvider) {
if (hostPart != "" && !Utils.checkHostPortParams(hostPart)) {
@ -718,6 +736,12 @@ class AccountActivity : AppCompatActivity() {
getString(R.string.voicemain_uri_help)
)
}
binding.CountryCodeTitle.setOnClickListener {
Utils.alertView(
this, getString(R.string.country_code),
getString(R.string.country_code_help)
)
}
binding.TelephonyProviderTitle.setOnClickListener {
Utils.alertView(
this, getString(R.string.telephony_provider),

View File

@ -172,7 +172,7 @@ class AccountsActivity : AppCompatActivity() {
accounts = accounts + a.print() + "\n"
}
Utils.putFileContents(BaresipService.filesPath + "/accounts", accounts.toByteArray())
// Log.d(TAG, "Saved accounts '${accounts}' to '${BaresipService.filesPath}/accounts'")
Log.d(TAG, "Saved accounts '${accounts}' to '${BaresipService.filesPath}/accounts'")
}
fun noAccounts(): Boolean {

View File

@ -680,7 +680,7 @@ class BaresipService: Service() {
PendingIntent.getActivity(applicationContext, CALL_REQ_CODE, intent,
PendingIntent.FLAG_UPDATE_CURRENT)
val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
val caller = Utils.friendlyUri(this, peerUri, aor)
val caller = Utils.friendlyUri(this, peerUri, ua.account, true)
nb.setSmallIcon(R.drawable.ic_stat_call)
.setColor(ContextCompat.getColor(this, R.color.colorBaresip))
.setContentIntent(pi)
@ -787,7 +787,7 @@ class BaresipService: Service() {
PendingIntent.getActivity(applicationContext, TRANSFER_REQ_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT)
val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
val target = Utils.friendlyUri(this, ev[1], aor)
val target = Utils.friendlyUri(this, ev[1], ua.account)
nb.setSmallIcon(R.drawable.ic_stat_call)
.setColor(ContextCompat.getColor(this, R.color.colorBaresip))
.setContentIntent(pi)
@ -868,7 +868,7 @@ class BaresipService: Service() {
}
if (!Utils.isVisible()) {
if (missed) {
val caller = Utils.friendlyUri(this, call.peerUri, aor)
val caller = Utils.friendlyUri(this, call.peerUri, ua.account)
val intent = Intent(applicationContext, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or
Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
@ -975,7 +975,7 @@ class BaresipService: Service() {
PendingIntent.getActivity(applicationContext, MESSAGE_REQ_CODE, intent,
PendingIntent.FLAG_UPDATE_CURRENT)
val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
val sender = Utils.friendlyUri(this, peer, ua.account.aor)
val sender = Utils.friendlyUri(this, peer, ua.account)
nb.setSmallIcon(R.drawable.ic_stat_message)
.setColor(ContextCompat.getColor(this, R.color.colorBaresip))
.setContentIntent(pi)

View File

@ -11,7 +11,8 @@ import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
class CallListAdapter(private val ctx: Context, private val aor: String, private val rows: ArrayList<CallRow>) :
class CallListAdapter(private val ctx: Context, private val account: Account,
private val rows: ArrayList<CallRow>) :
ArrayAdapter<CallRow>(ctx, R.layout.call_row, rows) {
private val layoutInflater = LayoutInflater.from(context)
@ -62,13 +63,18 @@ class CallListAdapter(private val ctx: Context, private val aor: String, private
if (count <= 3)
viewHolder.etcView.text = ""
viewHolder.peerURIView.text = Utils.friendlyUri(ctx, callRow.peerUri, callRow.aor)
var peer = Contact.contactName(callRow.peerUri)
if (peer == callRow.peerUri)
peer = Contact.contactName(Utils.e164Uri(peer, account.countryCode))
if (peer == callRow.peerUri)
peer = Utils.friendlyUri(ctx, peer, account, false)
viewHolder.peerURIView.text = peer
viewHolder.timeView.text = Utils.relativeTime(ctx, callRow.stopTime)
viewHolder.timeView.setOnClickListener {
val i = Intent(ctx, CallDetailsActivity::class.java)
val b = Bundle()
b.putString("aor", aor)
b.putString("aor", account.aor)
b.putString("peer", viewHolder.peerURIView.text!!.toString())
b.putInt("position", position)
i.putExtras(b)

View File

@ -40,13 +40,13 @@ class CallsActivity : AppCompatActivity() {
val listView = binding.calls
aorGenerateHistory(aor)
clAdapter = CallListAdapter(this, aor, uaHistory)
clAdapter = CallListAdapter(this, account, uaHistory)
listView.adapter = clAdapter
listView.isLongClickable = true
listView.onItemClickListener = AdapterView.OnItemClickListener { _, _, pos, _ ->
val peerUri = uaHistory[pos].peerUri
val peerName = Utils.friendlyUri(this, peerUri, aor)
val peerName = Utils.friendlyUri(this, peerUri, account)
val dialogClickListener = DialogInterface.OnClickListener { _, which ->
when (which) {
DialogInterface.BUTTON_POSITIVE, DialogInterface.BUTTON_NEGATIVE -> {
@ -119,7 +119,7 @@ class CallsActivity : AppCompatActivity() {
with (builder) {
setTitle(R.string.confirmation)
setMessage(String.format(getString(R.string.calls_add_delete_question),
Utils.friendlyUri(this@CallsActivity, peerName, aor), callText))
Utils.friendlyUri(this@CallsActivity, peerName, account), callText))
setNeutralButton(getString(R.string.cancel), dialogClickListener)
setPositiveButton(String.format(getString(R.string.delete), callText), dialogClickListener)
setNegativeButton(getString(R.string.add_contact), dialogClickListener)
@ -129,7 +129,7 @@ class CallsActivity : AppCompatActivity() {
with (builder) {
setTitle(R.string.confirmation)
setMessage(String.format(getString(R.string.calls_delete_question),
Utils.friendlyUri(this@CallsActivity, peerName, aor), callText))
Utils.friendlyUri(this@CallsActivity, peerName, account), callText))
setNeutralButton(getString(R.string.cancel), dialogClickListener)
setPositiveButton(String.format(getString(R.string.delete), callText), dialogClickListener)
show()

View File

@ -66,7 +66,7 @@ class ChatActivity : AppCompatActivity() {
imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
this@ChatActivity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
val chatPeer = Utils.friendlyUri(this, peerUri, aor)
val chatPeer = Utils.friendlyUri(this, peerUri, userAgent.account)
title = String.format(getString(R.string.chat_with), chatPeer)

View File

@ -14,7 +14,7 @@ import androidx.core.content.ContextCompat
import java.text.DateFormat
import java.util.*
class ChatListAdapter(private val ctx: Context, private val rows: ArrayList<Message>) :
class ChatListAdapter(private val ctx: Context, private val account: Account, private val rows: ArrayList<Message>) :
ArrayAdapter<Message>(ctx, R.layout.message, rows) {
private val layoutInflater = LayoutInflater.from(context)
@ -52,7 +52,7 @@ class ChatListAdapter(private val ctx: Context, private val rows: ArrayList<Mess
else
viewHolder.layoutView.setBackgroundResource(R.drawable.message_out_bg)
viewHolder.peerView.text = Utils.friendlyUri(ctx, message.peerUri, message.aor)
viewHolder.peerView.text = Utils.friendlyUri(ctx, message.peerUri, account)
val cal = GregorianCalendar()
cal.timeInMillis = message.timeStamp

View File

@ -42,7 +42,7 @@ class ChatsActivity: AppCompatActivity() {
headerView.text = headerText
uaMessages = uaMessages(aor)
clAdapter = ChatListAdapter(this, uaMessages)
clAdapter = ChatListAdapter(this, account, uaMessages)
listView.adapter = clAdapter
listView.isLongClickable = true
@ -51,7 +51,7 @@ class ChatsActivity: AppCompatActivity() {
if (it.resultCode == RESULT_OK) {
clAdapter.clear()
uaMessages = uaMessages(aor)
clAdapter = ChatListAdapter(this, uaMessages)
clAdapter = ChatListAdapter(this, account, uaMessages)
listView.adapter = clAdapter
}
}
@ -101,7 +101,7 @@ class ChatsActivity: AppCompatActivity() {
with (builder) {
setTitle(R.string.confirmation)
setMessage(String.format(getString(R.string.long_chat_question),
Utils.friendlyUri(this@ChatsActivity, peer, aor)))
Utils.friendlyUri(this@ChatsActivity, peer, account)))
setNeutralButton(getText(R.string.cancel), dialogClickListener)
setNegativeButton(getText(R.string.delete), dialogClickListener)
setPositiveButton(getText(R.string.add_contact), dialogClickListener)
@ -159,7 +159,7 @@ class ChatsActivity: AppCompatActivity() {
super.onResume()
clAdapter.clear()
uaMessages = uaMessages(aor)
clAdapter = ChatListAdapter(this, uaMessages)
clAdapter = ChatListAdapter(this, account, uaMessages)
listView.adapter = clAdapter
if (uaMessages.count() > 0) {
if (scrollPosition >= 0) {

View File

@ -1047,9 +1047,12 @@ class MainActivity : AppCompatActivity() {
if (aor == aorSpinner.tag) {
securityButton.setImageResource(security)
setSecurityButtonTag(securityButton, security)
securityButton.visibility = View.VISIBLE
dialog.dismiss()
securityButton.visibility = if (Call.ofCallp(callp) == null)
View.INVISIBLE
else
View.VISIBLE
}
dialog.dismiss()
}
setNeutralButton(getString(R.string.no)) { dialog, _ ->
call.security = R.drawable.box_yellow
@ -1057,7 +1060,10 @@ class MainActivity : AppCompatActivity() {
if (aor == aorSpinner.tag) {
securityButton.setImageResource(R.drawable.box_yellow)
securityButton.tag = "yellow"
securityButton.visibility = View.VISIBLE
securityButton.visibility = if (Call.ofCallp(callp) == null)
View.INVISIBLE
else
View.VISIBLE
}
dialog.dismiss()
}
@ -1094,7 +1100,7 @@ class MainActivity : AppCompatActivity() {
return
}
val call = Call.ofCallp(callp)!!
val target = Utils.friendlyUri(this, ev[1], aor)
val target = Utils.friendlyUri(this, ev[1], acc)
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
setTitle(R.string.transfer_request)
setMessage(String.format(getString(R.string.transfer_request_query),
@ -1778,7 +1784,7 @@ class MainActivity : AppCompatActivity() {
} else {
val latest = NewCallHistory.aorLatestHistory(aor)
if (latest != null)
callUri.setText(Utils.friendlyUri(this, latest.peerUri, aor))
callUri.setText(Utils.friendlyUri(this, latest.peerUri, ua.account))
}
}
}
@ -1823,7 +1829,7 @@ class MainActivity : AppCompatActivity() {
else
getString(R.string.outgoing_call_to_dots)
callTimer.visibility = View.INVISIBLE
callUri.setText(Utils.friendlyUri(this, call.peerUri, ua.account.aor))
callUri.setText(Utils.friendlyUri(this, call.peerUri, ua.account))
securityButton.visibility = View.INVISIBLE
diverter.visibility = View.GONE
callButton.visibility = View.INVISIBLE
@ -1838,12 +1844,14 @@ class MainActivity : AppCompatActivity() {
"incoming" -> {
callTitle.text = getString(R.string.incoming_call_from_dots)
callTimer.visibility = View.INVISIBLE
callUri.setText(Utils.friendlyUri(this, call.peerUri, ua.account.aor))
val caller = Utils.friendlyUri(this, call.peerUri, ua.account, true)
callUri.setText(caller)
callUri.setAdapter(null)
securityButton.visibility = View.INVISIBLE
val uri = call.diverterUri()
if (uri != "") {
diverterUri.text = Utils.friendlyUri(this, uri, ua.account.aor)
val diversionUri = Utils.friendlyUri(this, uri, ua.account, true)
diverterUri.text = diversionUri
diverter.visibility = View.VISIBLE
} else {
diverter.visibility = View.GONE
@ -1864,14 +1872,16 @@ class MainActivity : AppCompatActivity() {
}
if (call.referTo != "") {
callTitle.text = getString(R.string.transferring_call_to_dots)
callUri.setText(Utils.friendlyUri(this, call.referTo, ua.account.aor))
callUri.setText(Utils.friendlyUri(this, call.referTo, ua.account))
transferButton.isEnabled = false
} else {
if (call.dir == "out")
if (call.dir == "out") {
callTitle.text = getString(R.string.outgoing_call_to_dots)
else
callUri.setText(Utils.friendlyUri(this, call.peerUri, ua.account))
} else {
callTitle.text = getString(R.string.incoming_call_from_dots)
callUri.setText(Utils.friendlyUri(this, call.peerUri, ua.account.aor))
callUri.setText(Utils.friendlyUri(this, call.peerUri, ua.account, true))
}
transferButton.isEnabled = true
}
if (call.onHoldCall == null)

View File

@ -28,6 +28,7 @@ import android.widget.TextView
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.text.isDigitsOnly
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -120,10 +121,15 @@ object Utils {
return if (params.size == 1) listOf() else params.subList(1, params.size)
}
fun friendlyUri(ctx: Context, uri: String, aor: String): String {
fun friendlyUri(ctx: Context, uri: String, account: Account, e164Check: Boolean = false): String {
var u = Contact.contactName(uri)
if (u != uri)
return u
if (e164Check) {
u = Contact.contactName(e164Uri(uri, account.countryCode))
if (u != uri)
return u
}
val params = uriParams(u)
if (uri.startsWith("<") && (uri.endsWith(">")))
u = uri.substring(1).substringBeforeLast(">")
@ -136,7 +142,7 @@ object Utils {
return if (u.contains("@")) {
val user = uriUserPart(u)
val host = uriHostPart(u)
if (isTelNumber(user) || host == aorDomain(aor))
if (isTelNumber(user) || host == aorDomain(account.aor))
user
else
if (host == "anonymous.invalid")
@ -150,6 +156,20 @@ object Utils {
}
}
fun e164Uri(uri: String, countryCode: String): String {
if (countryCode == "") return uri
val userPart = uriUserPart(uri)
return if (userPart.isDigitsOnly()) {
when {
userPart.startsWith("00") -> uri.replace("sip:$userPart",
"sip:+" + userPart.substring(2))
userPart.startsWith("0") -> uri.replace("sip:0", "sip:$countryCode")
else -> uri.replace("sip:", "sip:$countryCode")
}
} else
uri
}
fun uriComplete(uri: String, aor: String): String {
val res = if (!uri.startsWith("sip:")) "sip:$uri" else uri
return if (checkUriUser(uri)) "$res@${aorDomain(aor)}" else res
@ -327,6 +347,11 @@ object Utils {
name.lines().size == 1 && !name.contains('"')
}
fun checkCountryCode(cc: String): Boolean {
return cc.startsWith("+") && cc.length > 1 && cc.length < 5 &&
cc.substring(1).isDigitsOnly() && cc[1] != '0'
}
fun setAvatar(ctx: Context, imageView: ImageView, textView: TextView, uri: String) {
when (val contact = Contact.findContact(uri)) {

View File

@ -301,6 +301,25 @@
android:layout_width="fill_parent">
</EditText>
<TextView
android:id="@+id/CountryCodeTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:text="@string/country_code" >
</TextView>
<EditText
android:id="@+id/countryCode"
android:layout_height="wrap_content"
android:hint="@string/country_code_hint"
android:textSize="18sp"
android:inputType="phone"
android:importantForAutofill="no"
android:layout_width="fill_parent">
</EditText>
<TextView
android:id="@+id/TelephonyProviderTitle"
android:layout_width="wrap_content"

View File

@ -204,6 +204,15 @@
mahdollista puhepostiviesteistä (Message Waiting Indications) ei tilata.
</string>
<string name="invalid_voicemail_uri">Virheellinen puhepostin URI \'%1$s\'</string>
<string name="country_code">Maakoodi</string>
<string name="country_code_help">Tämän tilin E.164-maakoodi. Jos tulevan puhelun From URI:n
etsintä yhteystiedoista epäonnistuu ja sen käyttäjäosa sisältää puhelinnumeron,
joka ei ala \'+\' merkillä, niin tämä maakoodi lisätään numeron eteen ja etsintä tehdään
uudelleen. Jos puhelinnumero alkaa yhdellä numerolla \'0\', niin numero \'0\'
poistetaan ennen maakoodin lisäämistä.
</string>
<string name="country_code_hint">+code</string>
<string name="invalid_country_code">Invalid Country Code \'%1$s\'</string>
<string name="telephony_provider">Puhelinpalvelun tarjoaja</string>
<string name="telephony_provider_help">SIP URI:n domain-osa, jota käytetään soitettaessa
puhelinnumeroihin. Tehdasasetus on tilin domain-osa. Jos tyhjä, niin tätä tiliä ei

View File

@ -182,6 +182,15 @@
messages (Message Waiting Indications) are not subscribed to.
</string>
<string name="invalid_voicemail_uri">Invalid Voicemail URI \'%1$s\'</string>
<string name="country_code">Country Code</string>
<string name="country_code_help">E.164 country code of this account. If From URI userpart of
incoming call contains a telephone number that does not start with \'+\' sign and if contact
lookup fails, the number is prefixed with this country code and contact lookup is
tried again. If the telephone number starts with a single digit \'0\', digit \'0\' is removed
before the number is prefixed.
</string>
<string name="country_code_hint">+code</string>
<string name="invalid_country_code">Invalid Country Code \'%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