- added support detecting (mwi) and listening of voicemails

- activated via account's voicemail uri
This commit is contained in:
Juha Heinanen
2018-09-18 13:41:53 +03:00
parent 0076fd9f5c
commit f6732395e2
13 changed files with 252 additions and 27 deletions

View File

@ -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'

View File

@ -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

View File

@ -10,6 +10,10 @@ class Account(val accp: String) {
var audioCodec = ArrayList<String>()
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<Account> {
@ -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
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

View File

@ -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<String>()
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())
}
}
}

View File

@ -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")

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
<vector android:height="48dp" android:tint="#04B404"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="48dp" android:tint="#04B404"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M18.5,6C15.46,6 13,8.46 13,11.5c0,1.33 0.47,2.55 1.26,3.5L9.74,15c0.79,-0.95 1.26,-2.17 1.26,-3.5C11,8.46 8.54,6 5.5,6S0,8.46 0,11.5 2.46,17 5.5,17h13c3.04,0 5.5,-2.46 5.5,-5.5S21.54,6 18.5,6zM5.5,15C3.57,15 2,13.43 2,11.5S3.57,8 5.5,8 9,9.57 9,11.5 7.43,15 5.5,15zM18.5,15c-1.93,0 -3.5,-1.57 -3.5,-3.5S16.57,8 18.5,8 22,9.57 22,11.5 20.43,15 18.5,15z"/>
</vector>

View File

@ -157,6 +157,26 @@
android:popupBackground="@drawable/spinner_background" >
</Spinner>
<TextView
android:id="@+id/VoicemailUriTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="Voicemail URI" >
</TextView>
<EditText
android:id="@+id/voicemailUri"
android:layout_height="wrap_content"
android:hint="SIP URI"
android:textSize="18sp"
android:inputType="textUri"
android:layout_width="fill_parent">
</EditText>
</LinearLayout>
</ScrollView>

View File

@ -149,6 +149,18 @@
</ScrollView>
<ImageButton
android:id="@+id/voicemailButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:src="@drawable/voicemail"
android:layout_toStartOf="@id/contactsButton"
android:layout_marginEnd="10dp"
android:layout_alignParentBottom="true"
android:visibility="invisible" >
</ImageButton>
<ImageButton
android:id="@+id/contactsButton"
android:layout_width="wrap_content"

View File

@ -38,5 +38,8 @@
EMPTY value means that media encryption is not supported and that media transport protocol
thus is RTP/AVP.
</string>
<string name="vmUri">SIP URI for checking of voicemail messages. If left empty, voicemail
messages (Message Waiting Indications) are not subscribed to.
</string>>
<string name="title_activity_message">MessageActivity</string>
</resources>