From f6732395e2c9c2d0a95df7ba9a5295a33fbc8e99 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Tue, 18 Sep 2018 13:41:53 +0300 Subject: [PATCH] - added support detecting (mwi) and listening of voicemails - activated via account's voicemail uri --- app/build.gradle | 4 +- app/src/main/assets/config | 3 +- .../main/kotlin/com/tutpro/baresip/Account.kt | 42 +++++- .../com/tutpro/baresip/AccountActivity.kt | 28 ++++ .../com/tutpro/baresip/BaresipService.kt | 24 +++- .../com/tutpro/baresip/CallsActivity.kt | 3 + .../kotlin/com/tutpro/baresip/MainActivity.kt | 129 +++++++++++++++--- .../com/tutpro/baresip/UaSpinnerAdapter.kt | 1 - app/src/main/res/drawable/calls_missed.xml | 5 + app/src/main/res/drawable/voicemail_new.xml | 5 + app/src/main/res/layout/activity_account.xml | 20 +++ app/src/main/res/layout/activity_main.xml | 12 ++ app/src/main/res/values/strings.xml | 3 + 13 files changed, 252 insertions(+), 27 deletions(-) create mode 100644 app/src/main/res/drawable/calls_missed.xml create mode 100644 app/src/main/res/drawable/voicemail_new.xml diff --git a/app/build.gradle b/app/build.gradle index 7ec12bcb..42e3c89a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId = 'com.tutpro.baresip' minSdkVersion 21 targetSdkVersion 27 - versionCode = 20 - versionName = '2.5.0' + versionCode = 21 + versionName = '3.0.0' externalNativeBuild { cmake { cFlags '-DHAVE_INTTYPES_H' diff --git a/app/src/main/assets/config b/app/src/main/assets/config index 4372de68..edb176c1 100644 --- a/app/src/main/assets/config +++ b/app/src/main/assets/config @@ -58,7 +58,6 @@ dns_server 8.8.8.8:53 #module stdio.so #module cons.so #module evdev.so -#module httpd.so # Audio codec Modules (in order) module opus.so @@ -138,7 +137,7 @@ module_app contact.so module_app debug_cmd.so #module_app dtmfio.so #module_app echo.so -#module_app mwi.so +module_app mwi.so #module_app natbd.so #module_app presence.so #module_app vidloop.so diff --git a/app/src/main/kotlin/com/tutpro/baresip/Account.kt b/app/src/main/kotlin/com/tutpro/baresip/Account.kt index 304d12c7..f76e470a 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Account.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Account.kt @@ -10,6 +10,10 @@ class Account(val accp: String) { var audioCodec = ArrayList() var regint = account_regint(accp) var mediaenc = account_mediaenc(accp) + var vmUri = account_vm_uri(accp) + var vmNew = 0 + var vmOld = 0 + var missedCalls = false init { var i = 0 @@ -69,11 +73,45 @@ class Account(val accp: String) { if (mediaenc != "") res = res + ";mediaenc=${mediaenc}" + if (vmUri == "") + res = res + ";mwi=no" + else + res = res + ";vm_uri=\"$vmUri\"" + res = res + ";ptime=20;regint=${regint};regq=0.5;pubint=0;answermode=manual" return res } + fun vmMessage() : String { + var new = "" + var old = "" + if (vmNew > 0) + if (vmNew == 1) + new = "$vmNew new message" + else + new = "$vmNew new messages" + if (vmOld > 0) + if (vmOld == 1) + old = "$vmOld old message" + else + old = "$vmOld old messages" + var msg = "You have" + if (new != "") { + msg = "$msg $new" + if (old != "") msg = "$msg and $old" + } else { + if (old != "") + msg = "$msg $old" + else + msg = "$msg no messages" + } + return "$msg." + } + + fun host() : String { + return aor.split("@")[1] + } companion object { fun accounts(): ArrayList { @@ -124,4 +162,6 @@ external fun account_regint(acc: String): Int external fun account_set_regint(acc: String, regint: Int): Int external fun account_mediaenc(acc: String): String external fun account_set_mediaenc(acc: String, mencid: String): Int -external fun account_set_audio_codecs(acc: String, codecs: String): Int \ No newline at end of file +external fun account_set_audio_codecs(acc: String, codecs: String): Int +external fun account_set_mwi(acc: String, value: String): Int +external fun account_vm_uri(acc: String): String diff --git a/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt index 04bef72c..6b341bef 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AccountActivity.kt @@ -9,6 +9,7 @@ import android.view.MenuItem import android.view.View import android.widget.LinearLayout.LayoutParams import android.widget.* +import kotlinx.android.synthetic.main.activity_account.* class AccountActivity : AppCompatActivity() { @@ -21,6 +22,7 @@ class AccountActivity : AppCompatActivity() { internal lateinit var outbound2: EditText internal lateinit var regCheck: CheckBox internal lateinit var mediaEnc: String + internal lateinit var vmUri: EditText private var newCodecs = ArrayList() private var save = false @@ -113,6 +115,10 @@ class AccountActivity : AppCompatActivity() { override fun onNothingSelected(parent: AdapterView<*>) { } } + + vmUri = findViewById(R.id.voicemailUri) as EditText + vmUri.setText(acc.vmUri) + } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -259,6 +265,24 @@ class AccountActivity : AppCompatActivity() { } } + var vmUri = voicemailUri.text.toString().trim() + if (vmUri != acc.vmUri) { + if (vmUri != "") { + if (!vmUri.startsWith("sip:")) vmUri = "sip:$vmUri" + if (!vmUri.contains("@")) vmUri = "$vmUri@${acc.host()}" + if (!Utils.checkSipUri(vmUri)) { + Utils.alertView(this, "Notice", + "Invalid Voicemail URI: $vmUri") + return false + } + account_set_mwi(acc.accp, "yes") + } else { + account_set_mwi(acc.accp, "no") + } + acc.vmUri = vmUri + save = true + } + if (save) { AccountsActivity.saveAccounts() if (UserAgent.ua_update_account(MainActivity.uas[uaIndex].uap) != 0) @@ -308,6 +332,10 @@ class AccountActivity : AppCompatActivity() { Utils.alertView(this, "Media Encryption", getText(R.string.mediaEnc).toString()) } + findViewById(R.id.VoicemailUriTitle) as TextView -> { + Utils.alertView(this, "Voicemail URI", + getText(R.string.vmUri).toString()) + } } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index cc3fe041..a5f95f38 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -23,8 +23,7 @@ import android.support.v4.content.LocalBroadcastManager import android.os.Build import android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE import java.nio.charset.StandardCharsets -import android.net.Uri -import android.provider.Settings +import java.io.InputStream class BaresipService: Service() { @@ -125,6 +124,25 @@ class BaresipService: Service() { Utils.copyAssetToFile(applicationContext, a, "$path/$a") } else { Log.d(LOG_TAG, "Asset $a already copied") + if (a == "config") { + val inputStream: InputStream = file.inputStream() + var contents = inputStream.bufferedReader().use { it.readText() } + inputStream.close() + var write = false + if (!contents.contains("zrtp_hash")) { + contents = "${contents}zrtp_hash yes\n" + write = true + } + if (contents.contains(Regex("#module_app[ ]+mwi.so"))) { + contents = contents.replace(Regex("#module_app[ ]+mwi.so"), + "module_app mwi.so") + write = true + } + if (write) { + Log.d(LOG_TAG, "Writing $contents") + Utils.putFileContents(file, contents) + } + } } } @@ -239,7 +257,7 @@ class BaresipService: Service() { } else { Log.d(LOG_TAG, "Ua ${ua.account.aor} is NOT registered") MainActivity.images.add(R.drawable.dot_yellow) - ua.register() + // ua.register() } val intent = Intent("service event") intent.putExtra("event", "ua added") diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt index 7d953a0a..fc4e4ac0 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt @@ -31,6 +31,8 @@ class CallsActivity : AppCompatActivity() { val listview = findViewById(R.id.calls) as ListView val aor = intent.extras.getString("aor") + val ua = Account.findUa(aor) + aorGenerateHistory(aor) val adapter = CallListAdapter(this, uaHistory) @@ -84,6 +86,7 @@ class CallsActivity : AppCompatActivity() { } listview.isLongClickable = true + ua!!.account.missedCalls = false } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index fb158051..fee60803 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -2,8 +2,7 @@ package com.tutpro.baresip import android.Manifest import android.app.NotificationManager -import android.content.BroadcastReceiver -import android.content.Intent +import android.content.* import android.support.v4.app.ActivityCompat import android.support.v4.content.ContextCompat import android.support.v7.app.AlertDialog @@ -12,8 +11,6 @@ import android.os.Bundle import android.util.Log import android.view.Menu import android.view.MenuItem -import android.content.Context -import android.content.IntentFilter import android.content.pm.PackageManager import android.graphics.Color import android.media.AudioManager @@ -46,6 +43,7 @@ class MainActivity : AppCompatActivity() { internal lateinit var answerButton: ImageButton internal lateinit var rejectButton: ImageButton internal lateinit var holdButton: ImageButton + internal lateinit var voicemailButton: ImageButton internal lateinit var contactsButton: ImageButton internal lateinit var messagesButton: ImageButton internal lateinit var callsButton: ImageButton @@ -84,6 +82,7 @@ class MainActivity : AppCompatActivity() { rejectButton = findViewById(R.id.rejectButton) as ImageButton holdButton = findViewById(R.id.holdButton) as ImageButton dtmf = findViewById(R.id.dtmf) as EditText + voicemailButton = findViewById(R.id.voicemailButton) as ImageButton contactsButton = findViewById(R.id.contactsButton) as ImageButton messagesButton = findViewById(R.id.messagesButton) as ImageButton callsButton = findViewById(R.id.callsButton) as ImageButton @@ -196,6 +195,19 @@ class MainActivity : AppCompatActivity() { } } } + if (acc.vmUri != "") { + if (acc.vmNew > 0) + voicemailButton.setImageResource(R.drawable.voicemail_new) + else + voicemailButton.setImageResource(R.drawable.voicemail) + voicemailButton.visibility = View.VISIBLE + } else { + voicemailButton.visibility = View.INVISIBLE + } + if (acc.missedCalls) + callsButton.setImageResource(R.drawable.calls_missed) + else + callsButton.setImageResource(R.drawable.calls) } override fun onNothingSelected(parent: AdapterView<*>) { @@ -339,14 +351,45 @@ class MainActivity : AppCompatActivity() { } } + voicemailButton.setOnClickListener { + if (aorSpinner.selectedItemPosition >= 0) { + val ua = uas[aorSpinner.selectedItemPosition] + val acc = ua.account + if (acc.vmUri != "") { + val dialogClickListener = DialogInterface.OnClickListener { _, which -> + when (which) { + DialogInterface.BUTTON_NEGATIVE -> { + val callIntent = Intent(this, MainActivity::class.java) + callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP) + callIntent.putExtra("action", "call") + callIntent.putExtra("uap", ua.uap) + callIntent.putExtra("peer", acc.vmUri) + startActivity(callIntent) + } + DialogInterface.BUTTON_POSITIVE -> { + } + } + } + val builder = AlertDialog.Builder(this@MainActivity, + R.style.Theme_AppCompat) + builder.setTitle("Voicemail Messages") + builder.setMessage(acc.vmMessage()) + .setPositiveButton("Cancel", dialogClickListener) + .setNegativeButton("Check", dialogClickListener) + .show() + } + } + } + contactsButton.setOnClickListener { val i = Intent(this@MainActivity, ContactsActivity::class.java) - val b = Bundle() - if (aorSpinner.selectedItemPosition >= 0) - b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor) - else - b.putString("aor", "") - i.putExtras(b) + val b = Bundle() + if (aorSpinner.selectedItemPosition >= 0) + b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor) + else + b.putString("aor", "") + i.putExtras(b) startActivityForResult(i, CONTACTS_CODE) } @@ -366,7 +409,7 @@ class MainActivity : AppCompatActivity() { val b = Bundle() b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor) i.putExtras(b) - startActivityForResult(i, HISTORY_CODE) + startActivityForResult(i, CALLS_CODE) } } @@ -439,6 +482,10 @@ class MainActivity : AppCompatActivity() { if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE) CallHistory.aorRemoveHistory(history, aor) history.add(CallHistory(aor, peer_uri, "in", false)) + acc.missedCalls = true + if (ua == uas[aorSpinner.selectedItemPosition]) { + callsButton.setImageResource(R.drawable.calls_missed) + } } else { Log.d("Baresip", "Incoming call $uap/$callp/$peer_uri") calls.add(new_call) @@ -600,9 +647,14 @@ class MainActivity : AppCompatActivity() { } calls.remove(call) if (!call.hasHistory) { - if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE) - CallHistory.aorRemoveHistory(history, aor) - history.add(CallHistory(aor, call.peerURI, call.dir,false)) + if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE) + CallHistory.aorRemoveHistory(history, aor) + history.add(CallHistory(aor, call.peerURI, call.dir,false)) + if (call.dir == "in") { + acc.missedCalls = true + if (ua == uas[aorSpinner.selectedItemPosition]) + callsButton.setImageResource(R.drawable.calls_missed) + } } if (calls.size == 0) am.mode = AudioManager.MODE_NORMAL if (am.isSpeakerphoneOn) { @@ -635,6 +687,22 @@ class MainActivity : AppCompatActivity() { startActivity(i) } } + "mwi notify" -> { + val lines = ev[1].split("\n") + for (line in lines) { + if (line.startsWith("Voice-Message:")) { + val counts = (line.split(" ")[1]).split("/") + acc.vmNew = counts[0].toInt() + acc.vmOld = counts[1].toInt() + break + } + } + if (ua == uas[aorSpinner.selectedItemPosition]) + if (acc.vmNew > 0) + voicemailButton.setImageResource(R.drawable.voicemail_new) + else + voicemailButton.setImageResource(R.drawable.voicemail) + } else -> Log.w("Baresip", "Unknown event '${ev[0]}'") } break @@ -727,7 +795,7 @@ class MainActivity : AppCompatActivity() { override fun onResume() { super.onResume() - // Log.d("Baresip", "Resumed") + Log.d("Baresip", "Resumed") imm.hideSoftInputFromWindow(callUri.windowToken, 0) visible = true if (answerCall != "") { @@ -814,10 +882,33 @@ class MainActivity : AppCompatActivity() { ACCOUNTS_CODE -> { uaAdapter.notifyDataSetChanged() + val acc = uas[aorSpinner.selectedItemPosition].account + if (acc.vmUri != "") { + if (acc.vmNew > 0) + voicemailButton.setImageResource(R.drawable.voicemail_new) + else + voicemailButton.setImageResource(R.drawable.voicemail) + voicemailButton.visibility = View.VISIBLE + } else { + voicemailButton.visibility = View.INVISIBLE + } baresipService.setAction("UpdateNotification") startService(baresipService) } + ACCOUNT_CODE -> { + val acc = uas[aorSpinner.selectedItemPosition].account + if (acc.vmUri != "") { + if (acc.vmNew > 0) + voicemailButton.setImageResource(R.drawable.voicemail_new) + else + voicemailButton.setImageResource(R.drawable.voicemail) + voicemailButton.visibility = View.VISIBLE + } else { + voicemailButton.visibility = View.INVISIBLE + } + } + CONTACTS_CODE -> { callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, ContactsActivity.contacts.map{Contact -> Contact.name})) @@ -834,7 +925,8 @@ class MainActivity : AppCompatActivity() { } } - HISTORY_CODE -> { + CALLS_CODE -> { + val acc = uas[aorSpinner.selectedItemPosition].account if (resultCode == RESULT_OK) { if (data != null) { (findViewById(R.id.uri) as EditText).setText(data.getStringExtra("peer_uri")) @@ -842,10 +934,11 @@ class MainActivity : AppCompatActivity() { } if (resultCode == RESULT_CANCELED) { Log.d("Baresip", "History canceled") - if (CallHistory.aorHistory(history, uas[aorSpinner.selectedItemPosition].account.aor) == 0) { + if (CallHistory.aorHistory(history, acc.aor) == 0) { holdButton.visibility = View.INVISIBLE } } + callsButton.setImageResource(R.drawable.calls) } ABOUT_CODE -> { @@ -972,7 +1065,7 @@ class MainActivity : AppCompatActivity() { const val ACCOUNTS_CODE = 1 const val CONTACTS_CODE = 2 const val EDIT_CONFIG_CODE = 3 - const val HISTORY_CODE = 4 + const val CALLS_CODE = 4 const val ABOUT_CODE = 5 const val ACCOUNT_CODE = 6 const val CONTACT_CODE = 7 diff --git a/app/src/main/kotlin/com/tutpro/baresip/UaSpinnerAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/UaSpinnerAdapter.kt index 37d94fb1..39d8b451 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/UaSpinnerAdapter.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/UaSpinnerAdapter.kt @@ -1,7 +1,6 @@ package com.tutpro.baresip import android.content.Context -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup diff --git a/app/src/main/res/drawable/calls_missed.xml b/app/src/main/res/drawable/calls_missed.xml new file mode 100644 index 00000000..c664d1cc --- /dev/null +++ b/app/src/main/res/drawable/calls_missed.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/voicemail_new.xml b/app/src/main/res/drawable/voicemail_new.xml new file mode 100644 index 00000000..e96451b0 --- /dev/null +++ b/app/src/main/res/drawable/voicemail_new.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml index b92b247e..28b82eeb 100644 --- a/app/src/main/res/layout/activity_account.xml +++ b/app/src/main/res/layout/activity_account.xml @@ -157,6 +157,26 @@ android:popupBackground="@drawable/spinner_background" > + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index d964f36c..4b2bbc1a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -149,6 +149,18 @@ + + + + SIP URI for checking of voicemail messages. If left empty, voicemail + messages (Message Waiting Indications) are not subscribed to. + > MessageActivity