From 39c8563d567d266dcf7c6dd6da269ba7314f7e91 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Sun, 10 Apr 2022 17:10:55 +0300 Subject: [PATCH] Added Country Code account configuration option --- .../main/kotlin/com/tutpro/baresip/Account.kt | 8 +++++ .../com/tutpro/baresip/AccountActivity.kt | 24 +++++++++++++ .../com/tutpro/baresip/AccountsActivity.kt | 2 +- .../com/tutpro/baresip/BaresipService.kt | 8 ++--- .../com/tutpro/baresip/CallListAdapter.kt | 12 +++++-- .../com/tutpro/baresip/CallsActivity.kt | 8 ++--- .../kotlin/com/tutpro/baresip/ChatActivity.kt | 2 +- .../com/tutpro/baresip/ChatListAdapter.kt | 4 +-- .../com/tutpro/baresip/ChatsActivity.kt | 8 ++--- .../kotlin/com/tutpro/baresip/MainActivity.kt | 34 ++++++++++++------- .../main/kotlin/com/tutpro/baresip/Utils.kt | 29 ++++++++++++++-- app/src/main/res/layout/activity_account.xml | 19 +++++++++++ app/src/main/res/values-fi/strings.xml | 9 +++++ app/src/main/res/values/strings.xml | 9 +++++ 14 files changed, 143 insertions(+), 33 deletions(-) diff --git a/app/src/main/kotlin/com/tutpro/baresip/Account.kt b/app/src/main/kotlin/com/tutpro/baresip/Account.kt index e0f43d8a..c3aa8221 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Account.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Account.kt @@ -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(";") + "\"" diff --git a/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt index f98d5ae9..b608d373 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt @@ -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), diff --git a/app/src/main/kotlin/com/tutpro/baresip/AccountsActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/AccountsActivity.kt index dc91a975..c8008c8c 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AccountsActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AccountsActivity.kt @@ -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 { diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index 77093cb9..f13da57f 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -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) diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt index 600f161b..72c71ec0 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt @@ -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) : +class CallListAdapter(private val ctx: Context, private val account: Account, + private val rows: ArrayList) : ArrayAdapter(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) diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt index d11db626..edd7d256 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt @@ -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() diff --git a/app/src/main/kotlin/com/tutpro/baresip/ChatActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/ChatActivity.kt index a8a2ebc9..a5062eaf 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ChatActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ChatActivity.kt @@ -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) diff --git a/app/src/main/kotlin/com/tutpro/baresip/ChatListAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/ChatListAdapter.kt index c213ae35..a195d28e 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ChatListAdapter.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ChatListAdapter.kt @@ -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) : +class ChatListAdapter(private val ctx: Context, private val account: Account, private val rows: ArrayList) : ArrayAdapter(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 0) { if (scrollPosition >= 0) { diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index 2f8ec52c..100122a3 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -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) diff --git a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt index 664b835e..df2af77e 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt @@ -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)) { diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml index 18a04a53..757890e3 100644 --- a/app/src/main/res/layout/activity_account.xml +++ b/app/src/main/res/layout/activity_account.xml @@ -301,6 +301,25 @@ android:layout_width="fill_parent"> + + + + + + Virheellinen puhepostin URI \'%1$s\' + Maakoodi + 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ä. + + +code + Invalid Country Code \'%1$s\' Puhelinpalvelun tarjoaja SIP URI:n domain-osa, jota käytetään soitettaessa puhelinnumeroihin. Tehdasasetus on tilin domain-osa. Jos tyhjä, niin tätä tiliä ei diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a9df797c..ca5e7d27 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -182,6 +182,15 @@ messages (Message Waiting Indications) are not subscribed to. Invalid Voicemail URI \'%1$s\' + Country Code + 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. + + +code + Invalid Country Code \'%1$s\' Telephony Provider 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