- 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' applicationId = 'com.tutpro.baresip'
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 27 targetSdkVersion 27
versionCode = 20 versionCode = 21
versionName = '2.5.0' versionName = '3.0.0'
externalNativeBuild { externalNativeBuild {
cmake { cmake {
cFlags '-DHAVE_INTTYPES_H' cFlags '-DHAVE_INTTYPES_H'

View File

@ -58,7 +58,6 @@ dns_server 8.8.8.8:53
#module stdio.so #module stdio.so
#module cons.so #module cons.so
#module evdev.so #module evdev.so
#module httpd.so
# Audio codec Modules (in order) # Audio codec Modules (in order)
module opus.so module opus.so
@ -138,7 +137,7 @@ module_app contact.so
module_app debug_cmd.so module_app debug_cmd.so
#module_app dtmfio.so #module_app dtmfio.so
#module_app echo.so #module_app echo.so
#module_app mwi.so module_app mwi.so
#module_app natbd.so #module_app natbd.so
#module_app presence.so #module_app presence.so
#module_app vidloop.so #module_app vidloop.so

View File

@ -10,6 +10,10 @@ class Account(val accp: String) {
var audioCodec = ArrayList<String>() var audioCodec = ArrayList<String>()
var regint = account_regint(accp) var regint = account_regint(accp)
var mediaenc = account_mediaenc(accp) var mediaenc = account_mediaenc(accp)
var vmUri = account_vm_uri(accp)
var vmNew = 0
var vmOld = 0
var missedCalls = false
init { init {
var i = 0 var i = 0
@ -69,11 +73,45 @@ class Account(val accp: String) {
if (mediaenc != "") res = res + ";mediaenc=${mediaenc}" 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" res = res + ";ptime=20;regint=${regint};regq=0.5;pubint=0;answermode=manual"
return res 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 { companion object {
fun accounts(): ArrayList<Account> { 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_set_regint(acc: String, regint: Int): Int
external fun account_mediaenc(acc: String): String external fun account_mediaenc(acc: String): String
external fun account_set_mediaenc(acc: String, mencid: String): Int 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.view.View
import android.widget.LinearLayout.LayoutParams import android.widget.LinearLayout.LayoutParams
import android.widget.* import android.widget.*
import kotlinx.android.synthetic.main.activity_account.*
class AccountActivity : AppCompatActivity() { class AccountActivity : AppCompatActivity() {
@ -21,6 +22,7 @@ class AccountActivity : AppCompatActivity() {
internal lateinit var outbound2: EditText internal lateinit var outbound2: EditText
internal lateinit var regCheck: CheckBox internal lateinit var regCheck: CheckBox
internal lateinit var mediaEnc: String internal lateinit var mediaEnc: String
internal lateinit var vmUri: EditText
private var newCodecs = ArrayList<String>() private var newCodecs = ArrayList<String>()
private var save = false private var save = false
@ -113,6 +115,10 @@ class AccountActivity : AppCompatActivity() {
override fun onNothingSelected(parent: AdapterView<*>) { override fun onNothingSelected(parent: AdapterView<*>) {
} }
} }
vmUri = findViewById(R.id.voicemailUri) as EditText
vmUri.setText(acc.vmUri)
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { 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) { if (save) {
AccountsActivity.saveAccounts() AccountsActivity.saveAccounts()
if (UserAgent.ua_update_account(MainActivity.uas[uaIndex].uap) != 0) if (UserAgent.ua_update_account(MainActivity.uas[uaIndex].uap) != 0)
@ -308,6 +332,10 @@ class AccountActivity : AppCompatActivity() {
Utils.alertView(this, "Media Encryption", Utils.alertView(this, "Media Encryption",
getText(R.string.mediaEnc).toString()) 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.os.Build
import android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE import android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import android.net.Uri import java.io.InputStream
import android.provider.Settings
class BaresipService: Service() { class BaresipService: Service() {
@ -125,6 +124,25 @@ class BaresipService: Service() {
Utils.copyAssetToFile(applicationContext, a, "$path/$a") Utils.copyAssetToFile(applicationContext, a, "$path/$a")
} else { } else {
Log.d(LOG_TAG, "Asset $a already copied") 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 { } else {
Log.d(LOG_TAG, "Ua ${ua.account.aor} is NOT registered") Log.d(LOG_TAG, "Ua ${ua.account.aor} is NOT registered")
MainActivity.images.add(R.drawable.dot_yellow) MainActivity.images.add(R.drawable.dot_yellow)
ua.register() // ua.register()
} }
val intent = Intent("service event") val intent = Intent("service event")
intent.putExtra("event", "ua added") intent.putExtra("event", "ua added")

View File

@ -31,6 +31,8 @@ class CallsActivity : AppCompatActivity() {
val listview = findViewById(R.id.calls) as ListView val listview = findViewById(R.id.calls) as ListView
val aor = intent.extras.getString("aor") val aor = intent.extras.getString("aor")
val ua = Account.findUa(aor)
aorGenerateHistory(aor) aorGenerateHistory(aor)
val adapter = CallListAdapter(this, uaHistory) val adapter = CallListAdapter(this, uaHistory)
@ -84,6 +86,7 @@ class CallsActivity : AppCompatActivity() {
} }
listview.isLongClickable = true listview.isLongClickable = true
ua!!.account.missedCalls = false
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {

View File

@ -2,8 +2,7 @@ package com.tutpro.baresip
import android.Manifest import android.Manifest
import android.app.NotificationManager import android.app.NotificationManager
import android.content.BroadcastReceiver import android.content.*
import android.content.Intent
import android.support.v4.app.ActivityCompat import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog import android.support.v7.app.AlertDialog
@ -12,8 +11,6 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.content.Context
import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Color import android.graphics.Color
import android.media.AudioManager import android.media.AudioManager
@ -46,6 +43,7 @@ class MainActivity : AppCompatActivity() {
internal lateinit var answerButton: ImageButton internal lateinit var answerButton: ImageButton
internal lateinit var rejectButton: ImageButton internal lateinit var rejectButton: ImageButton
internal lateinit var holdButton: ImageButton internal lateinit var holdButton: ImageButton
internal lateinit var voicemailButton: ImageButton
internal lateinit var contactsButton: ImageButton internal lateinit var contactsButton: ImageButton
internal lateinit var messagesButton: ImageButton internal lateinit var messagesButton: ImageButton
internal lateinit var callsButton: ImageButton internal lateinit var callsButton: ImageButton
@ -84,6 +82,7 @@ class MainActivity : AppCompatActivity() {
rejectButton = findViewById(R.id.rejectButton) as ImageButton rejectButton = findViewById(R.id.rejectButton) as ImageButton
holdButton = findViewById(R.id.holdButton) as ImageButton holdButton = findViewById(R.id.holdButton) as ImageButton
dtmf = findViewById(R.id.dtmf) as EditText dtmf = findViewById(R.id.dtmf) as EditText
voicemailButton = findViewById(R.id.voicemailButton) as ImageButton
contactsButton = findViewById(R.id.contactsButton) as ImageButton contactsButton = findViewById(R.id.contactsButton) as ImageButton
messagesButton = findViewById(R.id.messagesButton) as ImageButton messagesButton = findViewById(R.id.messagesButton) as ImageButton
callsButton = findViewById(R.id.callsButton) 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<*>) { 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 { contactsButton.setOnClickListener {
val i = Intent(this@MainActivity, ContactsActivity::class.java) val i = Intent(this@MainActivity, ContactsActivity::class.java)
val b = Bundle() val b = Bundle()
if (aorSpinner.selectedItemPosition >= 0) if (aorSpinner.selectedItemPosition >= 0)
b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor) b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor)
else else
b.putString("aor", "") b.putString("aor", "")
i.putExtras(b) i.putExtras(b)
startActivityForResult(i, CONTACTS_CODE) startActivityForResult(i, CONTACTS_CODE)
} }
@ -366,7 +409,7 @@ class MainActivity : AppCompatActivity() {
val b = Bundle() val b = Bundle()
b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor) b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor)
i.putExtras(b) 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) if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE)
CallHistory.aorRemoveHistory(history, aor) CallHistory.aorRemoveHistory(history, aor)
history.add(CallHistory(aor, peer_uri, "in", false)) history.add(CallHistory(aor, peer_uri, "in", false))
acc.missedCalls = true
if (ua == uas[aorSpinner.selectedItemPosition]) {
callsButton.setImageResource(R.drawable.calls_missed)
}
} else { } else {
Log.d("Baresip", "Incoming call $uap/$callp/$peer_uri") Log.d("Baresip", "Incoming call $uap/$callp/$peer_uri")
calls.add(new_call) calls.add(new_call)
@ -600,9 +647,14 @@ class MainActivity : AppCompatActivity() {
} }
calls.remove(call) calls.remove(call)
if (!call.hasHistory) { if (!call.hasHistory) {
if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE) if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE)
CallHistory.aorRemoveHistory(history, aor) CallHistory.aorRemoveHistory(history, aor)
history.add(CallHistory(aor, call.peerURI, call.dir,false)) 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 (calls.size == 0) am.mode = AudioManager.MODE_NORMAL
if (am.isSpeakerphoneOn) { if (am.isSpeakerphoneOn) {
@ -635,6 +687,22 @@ class MainActivity : AppCompatActivity() {
startActivity(i) 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]}'") else -> Log.w("Baresip", "Unknown event '${ev[0]}'")
} }
break break
@ -727,7 +795,7 @@ class MainActivity : AppCompatActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
// Log.d("Baresip", "Resumed") Log.d("Baresip", "Resumed")
imm.hideSoftInputFromWindow(callUri.windowToken, 0) imm.hideSoftInputFromWindow(callUri.windowToken, 0)
visible = true visible = true
if (answerCall != "") { if (answerCall != "") {
@ -814,10 +882,33 @@ class MainActivity : AppCompatActivity() {
ACCOUNTS_CODE -> { ACCOUNTS_CODE -> {
uaAdapter.notifyDataSetChanged() 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") baresipService.setAction("UpdateNotification")
startService(baresipService) 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 -> { CONTACTS_CODE -> {
callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
ContactsActivity.contacts.map{Contact -> Contact.name})) 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 (resultCode == RESULT_OK) {
if (data != null) { if (data != null) {
(findViewById(R.id.uri) as EditText).setText(data.getStringExtra("peer_uri")) (findViewById(R.id.uri) as EditText).setText(data.getStringExtra("peer_uri"))
@ -842,10 +934,11 @@ class MainActivity : AppCompatActivity() {
} }
if (resultCode == RESULT_CANCELED) { if (resultCode == RESULT_CANCELED) {
Log.d("Baresip", "History 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 holdButton.visibility = View.INVISIBLE
} }
} }
callsButton.setImageResource(R.drawable.calls)
} }
ABOUT_CODE -> { ABOUT_CODE -> {
@ -972,7 +1065,7 @@ class MainActivity : AppCompatActivity() {
const val ACCOUNTS_CODE = 1 const val ACCOUNTS_CODE = 1
const val CONTACTS_CODE = 2 const val CONTACTS_CODE = 2
const val EDIT_CONFIG_CODE = 3 const val EDIT_CONFIG_CODE = 3
const val HISTORY_CODE = 4 const val CALLS_CODE = 4
const val ABOUT_CODE = 5 const val ABOUT_CODE = 5
const val ACCOUNT_CODE = 6 const val ACCOUNT_CODE = 6
const val CONTACT_CODE = 7 const val CONTACT_CODE = 7

View File

@ -1,7 +1,6 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.content.Context import android.content.Context
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup 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" > android:popupBackground="@drawable/spinner_background" >
</Spinner> </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> </LinearLayout>
</ScrollView> </ScrollView>

View File

@ -149,6 +149,18 @@
</ScrollView> </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 <ImageButton
android:id="@+id/contactsButton" android:id="@+id/contactsButton"
android:layout_width="wrap_content" 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 EMPTY value means that media encryption is not supported and that media transport protocol
thus is RTP/AVP. thus is RTP/AVP.
</string> </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> <string name="title_activity_message">MessageActivity</string>
</resources> </resources>