- Replaced text mode EditConfigActivity with graphical Config activity

- Current configuration items are Auto Start, DNS Servers, Opus Bit Rate,
  and ICE Lite
- Added ICE and Stun Server account options
- Fixed bug in deleting of an account
- Updated libbaresip.a and baresip.h to baresip new_account_functions
  branch (not yet committed to master)
This commit is contained in:
Juha Heinanen
2018-11-30 02:07:03 +02:00
parent 6f652f3872
commit 4626967ae1
20 changed files with 664 additions and 385 deletions

View File

@ -7,8 +7,8 @@ android {
applicationId = 'com.tutpro.baresip'
minSdkVersion 21
targetSdkVersion 27
versionCode = 36
versionName = '4.1.2'
versionCode = 37
versionName = '5.0.0'
externalNativeBuild {
cmake {
cFlags '-DHAVE_INTTYPES_H'

View File

@ -8,7 +8,6 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-feature
android:name="android.hardware.telephony"
@ -67,8 +66,8 @@
android:parentActivityName=".ContactsActivity" >
</activity>
<activity
android:name=".EditConfigActivity"
android:label="Edit Config"
android:name=".ConfigActivity"
android:label="Config"
android:parentActivityName=".MainActivity" >
</activity>
<activity

View File

@ -1,171 +1,42 @@
#
# baresip configuration
#
auto_start no
poll_method epoll
sip_trans_bsize 128
call_local_timeout 120
call_max_calls 4
audio_player opensles,nil
audio_source opensles,nil
audio_alert opensles,nil
audio_srate 8000-48000
audio_channels 1-2
audio_level no
rtp_tos 184
rtcp_enable yes
rtcp_mux no
jitter_buffer_delay 5-10
rtp_stats no
dns_server 8.8.8.8:53
module opus.so
module g711.so
module opensles.so
module stun.so
module turn.so
module ice.so
module srtp.so
module dtls_srtp.so
module zrtp.so
module_tmp uuid.so
module_tmp account.so
module_app auloop.so
module_app contact.so
module_app debug_cmd.so
module_app mwi.so
evdev_device /dev/input/event0
opus_bitrate 28000
opus_cbr no
opus_inbandfec yes
ice_turn no
ice_debug no
ice_nomination regular
ice_mode full
zrtp_hash yes
#------------------------------------------------------------------------------
# Core
poll_method epoll # poll, select, epoll ..
# SIP
sip_trans_bsize 128
#sip_listen 0.0.0.0:5060
#sip_certificate cert.pem
# Call
call_local_timeout 120
call_max_calls 4
# Audio
audio_player opensles,nil
audio_source opensles,nil
audio_alert opensles,nil
#ausrc_srate 48000
#auplay_srate 48000
#ausrc_channels 0
#auplay_channels 0
audio_level no
# Video
#video_source v4l2,/dev/video0
#video_display x11,nil
video_size 352x288
video_bitrate 500000
video_fps 25
video_fullscreen yes
# AVT - Audio/Video Transport
rtp_tos 184
#rtp_ports 10000-20000
#rtp_bandwidth 512-1024 # [kbit/s]
rtcp_enable yes
rtcp_mux no
jitter_buffer_delay 5-10 # frames
rtp_stats no
#rtp_timeout 60
# Network
dns_server 8.8.8.8:53
#net_interface ^
# BFCP
#bfcp_proto udp
#------------------------------------------------------------------------------
# Modules
# UI Modules
#module stdio.so
#module cons.so
#module evdev.so
# Audio codec Modules (in order)
module opus.so
#module g7221.so
#module amr.so
#module g722.so
#module g726.so
module g711.so
#module l16.so
#module bv32.so
#module mpa.so
#module codec2.so
# Audio filter Modules (in encoding order)
#module vumeter.so
#module sndfile.so
#module plc.so
# Audio driver Modules
module opensles.so
#module aubridge.so
#module aufile.so
# Video codec Modules (in order)
#module avcodec.so
#module vp8.so
#module vp9.so
#module h265.so
# Video filter Modules (in encoding order)
#module selfview.so
#module snapshot.so
#module swscale.so
#module vidinfo.so
# Video source modules
#module v4l.so
#module v4l2.so
#module v4l2_codec.so
#module cairo.so
#module vidbridge.so
# Video display modules
#module directfb.so
#module sdl2.so
#module fakevideo.so
# Audio/Video source modules
#module rst.so
#module gst1.so
#module gst_video1.so
# Media NAT modules
module stun.so
module turn.so
module ice.so
#module natpmp.so
# Media encryption modules
module srtp.so
module dtls_srtp.so
module zrtp.so
#------------------------------------------------------------------------------
# Temporary Modules (loaded then unloaded)
module_tmp uuid.so
module_tmp account.so
#------------------------------------------------------------------------------
# Application Modules
module_app auloop.so
module_app contact.so
module_app debug_cmd.so
#module_app dtmfio.so
#module_app echo.so
module_app mwi.so
#module_app natbd.so
#module_app presence.so
#module_app vidloop.so
#------------------------------------------------------------------------------
# Module parameters
cons_listen 0.0.0.0:5555
http_listen 0.0.0.0:8000
evdev_device /dev/input/event0
# Opus codec parameters
opus_bitrate 28000 # 6000-510000
opus_cbr no
opus_inbandfec yes
# Selfview
#video_selfview window # {window,pip}
#selfview_size 64x64
# ICE
ice_turn no
ice_debug no
ice_nomination regular # {regular,aggressive}
ice_mode full # {full,lite}
# ZRTP
zrtp_hash yes

View File

@ -686,6 +686,36 @@ Java_com_tutpro_baresip_AccountKt_account_1set_1mediaenc(JNIEnv *env, jobject th
(*env)->ReleaseStringUTFChars(env, javaMencid, mencid);
return res;
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_AccountKt_account_1medianat(JNIEnv *env, jobject thiz, jstring javaAcc)
{
const char *native_acc = (*env)->GetStringUTFChars(env, javaAcc, 0);
struct account *acc = (struct account *) strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaAcc, native_acc);
if (acc) {
const char *medianat = account_medianat(acc);
if (medianat) return (*env)->NewStringUTF(env, medianat);
}
return (*env)->NewStringUTF(env, "");
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1medianat(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaMedNat) {
const char *native_acc = (*env)->GetStringUTFChars(env, javaAcc, 0);
struct account *acc = (struct account *)strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaAcc, native_acc);
const char *mednat = (*env)->GetStringUTFChars(env, javaMedNat, 0);
int res;
if (strlen(mednat) > 0)
res = account_set_medianat(acc, mednat);
else
res = account_set_medianat(acc, NULL);
(*env)->ReleaseStringUTFChars(env, javaMedNat, mednat);
return res;
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_AccountKt_account_1sipnat(JNIEnv *env, jobject thiz, jstring javaAcc)
{
@ -715,6 +745,56 @@ Java_com_tutpro_baresip_AccountKt_account_1set_1sipnat(JNIEnv *env, jobject thiz
return res;
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_AccountKt_account_1stun_1host(JNIEnv *env, jobject thiz, jstring javaAcc)
{
const char *native_acc = (*env)->GetStringUTFChars(env, javaAcc, 0);
struct account *acc = (struct account *) strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaAcc, native_acc);
if (acc) {
const char *stun_host = account_stun_host(acc);
if (stun_host) return (*env)->NewStringUTF(env, stun_host);
}
return (*env)->NewStringUTF(env, "");
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1stun_1host(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaStunHost) {
const char *native_acc = (*env)->GetStringUTFChars(env, javaAcc, 0);
struct account *acc = (struct account *)strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaAcc, native_acc);
const char *stun_host = (*env)->GetStringUTFChars(env, javaStunHost, 0);
int res;
if (strlen(stun_host) > 0)
res = account_set_stun_host(acc, stun_host);
else
res = account_set_stun_host(acc, NULL);
(*env)->ReleaseStringUTFChars(env, javaStunHost, stun_host);
return res;
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1stun_1port(JNIEnv *env, jobject thiz, jstring javaAcc)
{
const char *native_acc = (*env)->GetStringUTFChars(env, javaAcc, 0);
struct account *acc = (struct account *) strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaAcc, native_acc);
return account_stun_port(acc);
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1stun_1port(JNIEnv *env, jobject thiz,
jstring javaAcc, jint javaStunPort) {
const char *native_acc = (*env)->GetStringUTFChars(env, javaAcc, 0);
struct account *acc = (struct account *)strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaAcc, native_acc);
const uint16_t native_port = javaStunPort;
int res;
res = account_set_stun_port(acc, native_port);
return res;
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1mwi(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaValue) {

View File

@ -7,15 +7,25 @@ class Account(val accp: String) {
var authUser = account_auth_user(accp)
var authPass = account_auth_pass(accp)
var outbound = ArrayList<String>()
var mediaNat = account_medianat(accp)
var stunServer = ""
var audioCodec = ArrayList<String>()
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 {
val stunHost = account_stun_host(accp)
if (stunHost != "") {
val stunPort = account_stun_port(accp)
if (stunPort == 0)
stunServer = stunHost
else
stunServer = "$stunHost:$stunPort"
}
var i = 0
while (true) {
val ob = account_outbound(accp, i)
@ -59,6 +69,10 @@ class Account(val accp: String) {
res = res + ";sipnat=outbound"
}
if (mediaNat != "") res = res + ";medianat=${mediaNat}"
if (stunServer != "") res = res + ";stunserver=\"stun:@${stunServer}\""
if (audioCodec.size > 0) {
var first = true
res = res + ";audio_codecs="
@ -71,7 +85,7 @@ class Account(val accp: String) {
}
}
if (mediaenc != "") res = res + ";mediaenc=${mediaenc}"
if (mediaEnc != "") res = res + ";mediaenc=${mediaEnc}"
if (vmUri == "")
res = res + ";mwi=no"
@ -159,8 +173,14 @@ external fun account_set_sipnat(acc: String, sipnat: String): Int
external fun account_audio_codec(acc: String, ix: Int): String
external fun account_regint(acc: String): Int
external fun account_set_regint(acc: String, regint: Int): Int
external fun account_stun_host(acc: String): String
external fun account_stun_port(acc: String): Int
external fun account_set_stun_host(acc: String, host: String): Int
external fun account_set_stun_port(acc: String, port: Int): Int
external fun account_mediaenc(acc: String): String
external fun account_set_mediaenc(acc: String, mencid: String): Int
external fun account_set_mediaenc(acc: String, mediaenc: String): Int
external fun account_medianat(acc: String): String
external fun account_set_medianat(acc: String, medianat: 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

@ -1,6 +1,5 @@
package com.tutpro.baresip
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
@ -21,6 +20,8 @@ class AccountActivity : AppCompatActivity() {
internal lateinit var authPass: EditText
internal lateinit var outbound1: EditText
internal lateinit var outbound2: EditText
internal lateinit var iceCheck: CheckBox
internal lateinit var stunServer: EditText
internal lateinit var regCheck: CheckBox
internal lateinit var mediaEnc: String
internal lateinit var vmUri: EditText
@ -56,6 +57,16 @@ class AccountActivity : AppCompatActivity() {
outbound2.setText(acc.outbound[1])
}
iceCheck = findViewById(R.id.Ice) as CheckBox
iceCheck.isChecked = acc.mediaNat == "ice"
iceCheck.setOnClickListener {
stunServer.isEnabled = iceCheck.isChecked
}
stunServer = findViewById(R.id.StunServer) as EditText
stunServer.setText(acc.stunServer)
stunServer.isEnabled = iceCheck.isChecked
regCheck = findViewById(R.id.Register) as CheckBox
regCheck.isChecked = acc.regint > 0
@ -95,15 +106,15 @@ class AccountActivity : AppCompatActivity() {
}
}
mediaEnc = acc.mediaenc
mediaEnc = acc.mediaEnc
val mediaEncSpinner = findViewById(R.id.mediaEncSpinner) as Spinner
val mediaEncKeys = arrayListOf("zrtp", "dtls_srtpf", "srtp-mand", "srtp", "")
val mediaEncVals = arrayListOf("ZRTP", "DTLS-SRTPF", "SRTP-MAND", "SRTP", "")
val keyIx = mediaEncKeys.indexOf(acc.mediaenc)
val keyIx = mediaEncKeys.indexOf(acc.mediaEnc)
val keyVal = mediaEncVals.elementAt(keyIx)
mediaEncKeys.removeAt(keyIx)
mediaEncVals.removeAt(keyIx)
mediaEncKeys.add(0, acc.mediaenc)
mediaEncKeys.add(0, acc.mediaEnc)
mediaEncVals.add(0, keyVal)
val mediaEncAdapter = ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,
mediaEncVals)
@ -131,8 +142,6 @@ class AccountActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val intent = Intent(this, MainActivity::class.java)
if (item.itemId == R.id.checkIcon) {
val dn = displayName.text.toString().trim()
@ -170,7 +179,7 @@ class AccountActivity : AppCompatActivity() {
val ap = authPass.text.toString().trim()
if (ap != acc.authPass) {
if (Utils.checkPrintASCII(ap)) {
if (Utils.checkPrintAscii(ap)) {
if (account_set_auth_pass(acc.accp, ap) == 0) {
acc.authPass = account_auth_pass(acc.accp)
// Log.d("Baresip", "New auth password is ${acc.authPass}")
@ -234,6 +243,46 @@ class AccountActivity : AppCompatActivity() {
Log.e("Baresip", "Setting of regint failed")
}
var newMediaNat = ""
if (iceCheck.isChecked) newMediaNat = "ice"
if (acc.mediaNat != newMediaNat)
if (account_set_medianat(acc.accp, newMediaNat) == 0) {
acc.mediaNat = account_medianat(acc.accp)
Log.d("Baresip", "New medianat is ${acc.mediaNat}")
save = true
} else {
Log.e("Baresip", "Setting of medianat failed")
}
if (iceCheck.isChecked) {
val newStunServer = stunServer.text.toString().trim()
if (acc.stunServer != newStunServer) {
if ((newStunServer != "") &&
!Utils.checkHostPort(newStunServer, false)) {
Utils.alertView(this, "Notice",
"Invalid STUN Server: $newStunServer")
return false
}
var host = ""
var port = ""
if (newStunServer != "") {
val hostPort = newStunServer.split(":")
host = hostPort[0]
if (hostPort.size == 2) port = hostPort[1]
}
if ((account_set_stun_host(acc.accp, host) == 0) &&
(account_set_stun_port(acc.accp, port.toInt()) == 0)) {
acc.stunServer = account_stun_host(acc.accp)
if (port != "")
acc.stunServer += ":" + account_stun_port(acc.accp).toString()
Log.d("Baresip", "New StunServer is ${acc.stunServer}")
save = true
} else {
Log.e("Baresip", "Setting of StunServer failed")
}
}
}
val ac = ArrayList(LinkedHashSet<String>(newCodecs.filter { it != "" } as ArrayList<String>))
if (ac != acc.audioCodec) {
Log.d("Baresip", "New codecs ${newCodecs.filter { it != "" }}")
@ -256,10 +305,10 @@ class AccountActivity : AppCompatActivity() {
}
}
if (mediaEnc != acc.mediaenc) {
if (mediaEnc != acc.mediaEnc) {
if (account_set_mediaenc(acc.accp, mediaEnc) == 0) {
acc.mediaenc = account_mediaenc(acc.accp)
Log.d("Baresip", "New mediaenc is ${acc.mediaenc}")
acc.mediaEnc = account_mediaenc(acc.accp)
Log.d("Baresip", "New mediaenc is ${acc.mediaEnc}")
save = true
} else {
Log.e("Baresip", "Setting of mediaenc $mediaEnc failed")
@ -324,6 +373,12 @@ class AccountActivity : AppCompatActivity() {
findViewById(R.id.RegTitle) as TextView -> {
Utils.alertView(this, "Register", getString(R.string.register))
}
findViewById(R.id.IceTitle) as TextView -> {
Utils.alertView(this, "Use ICE", getString(R.string.ice))
}
findViewById(R.id.StunServerTitle) as TextView -> {
Utils.alertView(this, "STUN Server", getString(R.string.stunServer))
}
findViewById(R.id.AudioCodecsTitle) as TextView -> {
Utils.alertView(this, "Audio Codecs", getString(R.string.auCodecs))
}

View File

@ -40,7 +40,7 @@ class AccountsActivity : AppCompatActivity() {
} else if (Account.exists(aor)) {
Log.e("Baresip", "Account $aor already exists")
} else {
val ua = UserAgent.uaAlloc("<$aor>;regq=0.5;pubint=0;regint=0")
val ua = UserAgent.uaAlloc("<$aor>;stunserver=\"stun:stun.l.google.com:19302\";regq=0.5;pubint=0;regint=0")
if (ua == null) {
Log.e("Baresip", "Failed to allocate UA for $aor")
Utils.alertView(this, "Notice",
@ -101,7 +101,7 @@ class AccountsActivity : AppCompatActivity() {
var accounts = ""
for (a in Account.accounts()) accounts = accounts + a.print() + "\n"
Utils.putFileContents(File(filesPath + "/accounts"), accounts)
// Log.d("Baresip", "Saved accounts '${accounts}' to '${filesPath}/accounts")
// Log.d("Baresip", "Saved accounts '${accounts}' to '${filesPath}/accounts'")
}
}

View File

@ -0,0 +1,175 @@
package com.tutpro.baresip
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.*
import java.io.File
class ConfigActivity : AppCompatActivity() {
internal lateinit var configFile: File
internal lateinit var autoStart: CheckBox
internal lateinit var dnsServers: EditText
internal lateinit var opusBitRate: EditText
internal lateinit var iceLite: CheckBox
private var oldAutoStart = ""
private var oldDnsServers = ""
private var oldOpusBitrate = ""
private var oldIceMode = ""
private var save = false
private var config = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_config)
configFile = File(applicationContext.filesDir.absolutePath + "/config")
config = Utils.getFileContents(configFile)
if (config.length <= 100) {
Utils.alertView(this, "Internal Error", "Failed to read config file")
finish()
return
}
autoStart = findViewById(R.id.AutoStart) as CheckBox
val asCv = Utils.getNameValue(config, "auto_start")
oldAutoStart = if (asCv.size == 0) "no" else asCv[0]
autoStart.isChecked = oldAutoStart == "yes"
dnsServers = findViewById(R.id.DnsServers) as EditText
val dsCv = Utils.getNameValue(config, "dns_server")
var dsTv = ""
for (ds in dsCv) dsTv += ", $ds"
oldDnsServers = dsTv.trimStart(',').trimStart(' ')
dnsServers.setText(oldDnsServers)
opusBitRate = findViewById(R.id.OpusBitRate) as EditText
val obCv = Utils.getNameValue(config, "opus_bitrate")
oldOpusBitrate = if (obCv.size == 0) "28000" else obCv[0]
opusBitRate.setText(oldOpusBitrate)
iceLite = findViewById(R.id.IceLite) as CheckBox
val imCv = Utils.getNameValue(config, "ice_mode")
oldIceMode = if (imCv.size == 0) "full" else imCv[0]
iceLite.isChecked = oldIceMode == "lite"
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
val inflater = menuInflater
inflater.inflate(R.menu.check_icon, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val intent = Intent(this, MainActivity::class.java)
if (item.itemId == R.id.checkIcon) {
var autoStartString = "no"
if (autoStart.isChecked) autoStartString = "yes"
if (oldAutoStart != autoStartString) {
config = Utils.removeLinesStartingWithName(config, "auto_start")
config += "\nauto_start $autoStartString\n"
save = true
}
val dnsServers = dnsServers.text.toString().trim()
if (dnsServers != oldDnsServers) {
if (!checkDnsServers(dnsServers)) {
Utils.alertView(this, "Notice", "Invalid DNS Servers: $dnsServers")
return false
}
config = Utils.removeLinesStartingWithName(config, "dns_server")
for (server in dnsServers.split(","))
config += "\ndns_server ${server.trim()}\n"
save = true
}
val opusBitRate = opusBitRate.text.toString().trim()
if (opusBitRate != oldOpusBitrate) {
if (!checkOpusBitRate(opusBitRate)) {
Utils.alertView(this, "Notice", "Invalid Opus Bit Rate: $opusBitRate")
return false
}
config = Utils.removeLinesStartingWithName(config, "opus_bitrate")
config += "\nopus_bitrate $opusBitRate\n"
save = true
}
var iceModeString = "full"
if (iceLite.isChecked) iceModeString = "lite"
if (oldIceMode != iceModeString) {
config = Utils.removeLinesStartingWithName(config, "ice_mode")
config += "\nice_mode $iceModeString\n"
save = true
}
if (save) {
var newConfig = ""
for (line in config.split("\n")) {
val trimmedLine = line.trim()
if (trimmedLine.startsWith("#") || (trimmedLine.length == 0)) continue
// Log.d("Baresip", "Config line $trimmedLine")
newConfig += trimmedLine.split("#")[0] + "\n"
}
Utils.putFileContents(configFile, newConfig)
// Api.reload_config()
}
setResult(RESULT_OK, intent)
finish()
return true
} else if (item.itemId == android.R.id.home) {
Log.d("Baresip", "Back array was pressed at Config")
setResult(Activity.RESULT_CANCELED, intent)
finish()
return true
} else return super.onOptionsItemSelected(item)
}
fun onClick(v: View) {
when (v) {
findViewById(R.id.AutoStartTitle) as TextView-> {
Utils.alertView(this, "Start Automatically", getString(R.string.autoStart))
}
findViewById(R.id.DnsServersTitle) as TextView -> {
Utils.alertView(this, "DNS Servers", getString(R.string.dnsServers))
}
findViewById(R.id.OpusBitRateTitle) as TextView-> {
Utils.alertView(this, "Opus Bit Rate", getString(R.string.opusBitRate))
}
findViewById(R.id.IceLiteTitle) as TextView-> {
Utils.alertView(this, "ICE Lite Mode", getString(R.string.iceLite))
}
}
}
private fun checkDnsServers(dnsServers: String): Boolean {
if (dnsServers.length == 0) return true
for (server in dnsServers.split(","))
if (!Utils.checkHostPort(server.trim(), true)) return false
return true
}
private fun checkOpusBitRate(opusBitRate: String): Boolean {
val number = opusBitRate.toIntOrNull()
if (number == null) return false
return (number >=6000) && (number <= 510000)
}
}

View File

@ -1,106 +0,0 @@
package com.tutpro.baresip
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import android.widget.EditText
import android.widget.TextView
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.OutputStreamWriter
class EditConfigActivity : AppCompatActivity() {
private var editText: EditText? = null
private var path: String? = null
private var file: File? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_config)
editText = findViewById(R.id.editConfig) as EditText
path = "${applicationContext.filesDir.absolutePath}/config"
file = File(path!!)
var content: String
if (!file!!.exists()) {
Log.e("Baresip", "Failed to find config file")
content = "No config"
} else {
Log.e("Baresip", "Found config file")
val length = file!!.length().toInt()
val bytes = ByteArray(length)
try {
val `in` = FileInputStream(file!!)
try {
`in`.read(bytes)
} finally {
`in`.close()
}
content = String(bytes)
} catch (e: java.io.IOException) {
Log.e("Baresip", "Failed to read config file: " + e.toString())
content = "Failed to read account file"
}
}
Log.d("Baresip", "Content length is: " + content.length)
editText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX,
resources.getDimension(R.dimen.textsize))
editText!!.setText(content, TextView.BufferType.EDITABLE)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
val inflater = menuInflater
inflater.inflate(R.menu.check_icon, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val i = Intent(this, MainActivity::class.java)
if (item.itemId == R.id.checkIcon) {
try {
val fOut = FileOutputStream(file!!.absoluteFile, false)
val fWriter = OutputStreamWriter(fOut)
val res = editText!!.text.toString()
try {
fWriter.write(res)
fWriter.close()
fOut.close()
} catch (e: java.io.IOException) {
Log.e("Baresip", "Failed to write config file: " + e.toString())
}
} catch (e: java.io.FileNotFoundException) {
Log.e("Baresip", "Failed to find config file: " + e.toString())
}
Log.d("Baresip", "Updated config file")
setResult(RESULT_OK, i)
finish()
return true
} else if (item.itemId == android.R.id.home) {
Log.d("Baresip", "Back array was pressed at Edit Config")
setResult(Activity.RESULT_CANCELED, i)
finish()
return true
} else return super.onOptionsItemSelected(item)
}
}

View File

@ -486,7 +486,7 @@ class MainActivity : AppCompatActivity() {
callTitle.text = "Incoming call from ..."
else
callTitle.text = "Outgoing call to ..."
if (acc.mediaenc == "") {
if (acc.mediaEnc == "") {
securityButton.visibility = View.INVISIBLE
} else {
securityButton.setImageResource(R.drawable.box_red)
@ -819,6 +819,11 @@ class MainActivity : AppCompatActivity() {
R.color.colorPrimary))
return true
}
R.id.config -> {
i = Intent(this, ConfigActivity::class.java)
startActivityForResult(i, CONFIG_CODE)
return true
}
R.id.accounts -> {
i = Intent(this, AccountsActivity::class.java)
val b = Bundle()
@ -827,11 +832,6 @@ class MainActivity : AppCompatActivity() {
startActivityForResult(i, ACCOUNTS_CODE)
return true
}
R.id.config -> {
i = Intent(this, EditConfigActivity::class.java)
startActivityForResult(i, EDIT_CONFIG_CODE)
return true
}
R.id.about -> {
Api.cmd_exec("audio_debug")
i = Intent(this, AboutActivity::class.java)
@ -901,15 +901,12 @@ class MainActivity : AppCompatActivity() {
Contact.contacts().map{Contact -> Contact.name}))
}
EDIT_CONFIG_CODE -> {
if (resultCode == RESULT_OK) {
CONFIG_CODE -> {
if (resultCode == RESULT_OK)
Utils.alertView(this, "Notice",
"You need to restart baresip in order to activate saved config!")
Api.reload_config()
}
if (resultCode == RESULT_CANCELED) {
Log.d("Baresip", "Edit config canceled")
}
if (resultCode == RESULT_CANCELED)
Log.d("Baresip", "Config canceled")
}
CALLS_CODE -> {
@ -1078,7 +1075,7 @@ class MainActivity : AppCompatActivity() {
"connected" -> {
securityButton.setImageResource(call.security)
setSecurityButtonTag(securityButton, call.security)
if ((ua.account.mediaenc == "zrtp") || (ua.account.mediaenc == "dtls_srtpf"))
if ((ua.account.mediaEnc == "zrtp") || (ua.account.mediaEnc == "dtls_srtpf"))
securityButton.visibility = View.VISIBLE
else
securityButton.visibility = View.INVISIBLE
@ -1109,7 +1106,7 @@ class MainActivity : AppCompatActivity() {
const val ACCOUNTS_CODE = 1
const val CONTACTS_CODE = 2
const val EDIT_CONFIG_CODE = 3
const val CONFIG_CODE = 3
const val CALLS_CODE = 4
const val ABOUT_CODE = 5
const val ACCOUNT_CODE = 6

View File

@ -6,18 +6,25 @@ import android.content.Intent
import android.os.Bundle
import android.util.Log
import java.io.File
class RunOnStartup : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if ((intent.action == Intent.ACTION_BOOT_COMPLETED) or
(intent.action == "com.tutpro.baresip.Restart")) {
Log.d("Baresip", "Start baresip upon boot completed or restart")
val i = Intent(context, MainActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val b = Bundle()
b.putBoolean("onStartup", true)
i.putExtras(b)
context.startActivity(i)
val configFile = File(context.filesDir.absolutePath + "/config")
val config = Utils.getFileContents(configFile)
val asCv = Utils.getNameValue(config,"auto_start")
if ((asCv.size > 0) && (asCv[0] == "yes")) {
Log.d("Baresip", "Start baresip upon boot completed or restart")
val i = Intent(context, MainActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val b = Bundle()
b.putBoolean("onStartup", true)
i.putExtras(b)
context.startActivity(i)
}
}
}

View File

@ -36,7 +36,7 @@ class UserAgent (val uap: String) {
fun remove(ua: UserAgent) {
val index = BaresipService.uas.indexOf(ua)
if (index != -1) {
BaresipService.uas[index].destroy()
BaresipService.uas.removeAt(index)
BaresipService.status.removeAt(index)
}
}

View File

@ -18,7 +18,6 @@ object Utils {
Log.e("Baresip", "Failed to find file: " + file.path)
return ""
} else {
Log.e("Baresip", "Found file: " + file.path)
val length = file.length().toInt()
val bytes = ByteArray(length)
try {
@ -56,6 +55,23 @@ object Utils {
}
fun getNameValue(string: String, name: String): ArrayList<String> {
val lines = string.split("\n")
val result = ArrayList<String>()
for (line in lines) {
if (line.startsWith(name))
result.add((line.substring(name.length).trim()).split(" \t")[0])
}
return result
}
fun removeLinesStartingWithName(string: String, name: String): String {
var result = ""
for (line in string.split("\n"))
if (!line.startsWith(name)) result += line + "\n"
return result
}
fun copyAssetToFile(context: Context, asset: String, path: String) {
try {
val `is` = context.assets.open(asset)
@ -131,35 +147,49 @@ object Utils {
fun checkDomain(domain: String): Boolean {
val parts = domain.split(".")
for (p in parts) {
if (p.length == 0 || p.endsWith("-") || !Regex("^[a-zA-z]([-]|[a-zA-Z0-9])+\$").matches(p))
if ((p.length == 0) || p.endsWith("-") ||
!Regex("^[a-zA-z]([-a-zA-Z0-9])*\$").matches(p))
return false
}
return true
}
fun checkPort(p: String) : Boolean {
val number = p.toIntOrNull()
fun checkPort(port: String): Boolean {
val number = port.toIntOrNull()
if (number == null) return false
return (number > 0) and (number < 65536)
return (number > 0) && (number < 65536)
}
fun checkHostPort(hp: String) : Boolean {
fun checkHostPort(hp: String, portMandatory: Boolean) : Boolean {
val parts = hp.split(":")
if (portMandatory && (parts.size != 2)) return false
if (parts.size == 1) return checkIP(parts[0]) || checkDomain(parts[0])
return checkPort(parts[1]) && (checkIP(parts[0]) || checkDomain(parts[0]))
}
fun checkParams(p: String) : Boolean {
/* todo: proper check */
fun checkParams(params: String): Boolean {
for (param in params.split(";"))
if (!checkParam(param)) return false
return true
}
fun checkParam(param: String): Boolean {
val nameValue = param.split("=")
if (nameValue.size == 1)
/* Todo: do proper check */
return true
if (nameValue.size == 2)
/* Todo: do proper check */
return true
return false
}
fun checkHostPortParams(hpp: String) : Boolean {
val restParams = hpp.split(";", limit = 2)
if (restParams.size == 1)
return checkHostPort(restParams[0])
return checkHostPort(restParams[0], false)
else
return checkHostPort(restParams[0]) && checkParams(restParams[1])
return checkHostPort(restParams[0], false) && checkParams(restParams[1])
}
fun checkSipUri(uri: String): Boolean {
@ -186,15 +216,9 @@ object Utils {
return checkHostPortParams(uri.substring(4))
}
fun checkPrintASCII(s: String): Boolean {
fun checkPrintAscii(s: String): Boolean {
if (s == "") return true
val printASCIIRegex = Regex("^[ -~]*\$")
return printASCIIRegex.matches(s)
}
fun checkUint(s: String): Boolean {
val uintRegex = Regex("^[0-9]+\$")
return uintRegex.matches(s)
return Regex("^[ -~]*\$").matches(s)
}
fun checkName(name: String): Boolean {

View File

@ -98,10 +98,10 @@
android:hint="SIP URI of another Proxy Server"
android:textSize="18sp"
android:inputType="textEmailAddress"
android:layout_width="fill_parent">
android:layout_width="fill_parent" >
</EditText>
<LinearLayout
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
@ -109,6 +109,7 @@
android:id="@+id/RegTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
@ -118,10 +119,56 @@
android:id="@+id/Register"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:layout_alignParentEnd="true"
android:layout_gravity="end"
android:checked="false" >
</CheckBox>
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/IceTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="Use ICE" >
</TextView>
<CheckBox
android:id="@+id/Ice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_gravity="end"
android:checked="false" >
</CheckBox>
</RelativeLayout>
<TextView
android:id="@+id/StunServerTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textSize="18sp"
android:textColor="@android:color/black"
android:text="STUN Server"
android:onClick="onClick" >
</TextView>
<EditText
android:id="@+id/StunServer"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_marginStart="10dp"
android:textSize="18sp"
android:scrollHorizontally="true"
android:inputType="textEmailAddress" >
</EditText>
<TextView
android:id="@+id/AudioCodecsTitle"

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ConfigView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:orientation="vertical"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:paddingBottom="24dp"
android:focusableInTouchMode="true" >
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/AutoStartTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="Start Automatically" >
</TextView>
<CheckBox
android:id="@+id/AutoStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_gravity="end"
android:checked="false" >
</CheckBox>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/DnsServersTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="DNS Servers" >
</TextView>
<EditText
android:id="@+id/DnsServers"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_toEndOf="@+id/DnsServersTitle"
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
android:textSize="18sp"
android:scrollHorizontally="true" >
</EditText>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/OpusBitRateTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="Opus Bit Rate" >
</TextView>
<EditText
android:id="@+id/OpusBitRate"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/OpusBitRateTitle"
android:layout_marginStart="10dp"
android:textSize="18sp"
android:scrollHorizontally="true"
android:inputType="numberDecimal" >
</EditText>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/IceLiteTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:textSize="18sp"
android:textColor="@android:color/black"
android:onClick="onClick"
android:text="ICE Lite Mode" >
</TextView>
<CheckBox
android:id="@+id/IceLite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_gravity="end"
android:checked="false" >
</CheckBox>
</RelativeLayout>
</LinearLayout>
</ScrollView>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.tutpro.baresip.EditAccountsActivity">
<EditText
android:id="@+id/editAccounts"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.tutpro.baresip.EditConfigActivity">
<EditText
android:id="@+id/editConfig"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>

View File

@ -1,11 +1,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/accounts"
android:title="Accounts" />
<item android:id="@+id/config"
android:title="Config" />
<item android:id="@+id/accounts"
android:title="Accounts" />
<item android:id="@+id/about"
android:title="About" />

View File

@ -5,9 +5,8 @@
<b>Baresip based Android application</b>\n\n
Juha Heinanen jh@tutpro.com\n\n
<b>Usage Hints</b>\n\n
- start by creating an account (click item titles for help)\n
- usually there is no need to edit the config\n
- in config, dns_server by default points to 8.8.8.8:53 for Android 8+ usage\n
- check that config default values meet your needs (click item titles for help)\n
- then create one or more accounts (again click item titles for help)\n
- peers of calls and messages can be added to contacts by long clicks\n
- long clicks can also be used to remove calls, chats, messages, and contacts\n
- call click when callee has not been given can be used for re-dial\n
@ -25,6 +24,13 @@
<string name="register">If checked, registration is enabled and REGISTER requests are sent at
12 minute intervals.
</string>
<string name="ice">If checked, ICE (Interactive Connectivity Establishment) is enabled for NAT
traversal.
</string>
<string name="stunServer">A specific Stun Server used by ICE of form host[:port].
Factory default value is \'stun.l.google.com:19302\', pointing to public Google STUN server.
Username and password are currently not supported.
</string>
<string name="auCodecs">List of supported audio codecs in priority order.</string>
<string name="mediaEnc">Selects media transport encryption protocol.\n
ZRTP (recommended) means that ZRTP end-to-end media encryption negotiation is tried after
@ -41,5 +47,10 @@
<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>
<string name="autoStart">If checked, baresip starts automatically after device (re)start.</string>
<string name="dnsServers">Comma separated list of DNS servers. Each DNS server is of form
server:port. Factory default value is \'8.8.8.8:53\' pointing to public Google DNS server.</string>
<string name="opusBitRate">Average maximum bit rate used by Opus audio stream.
Valid value are 6000-510000. Factory default is 28000.</string>
<string name="iceLite">If checked, ICE Lite Mode is used.</string>
</resources>

View File

@ -0,0 +1,6 @@
- Replaced text mode EditConfigActivity with graphical Config activity
- Current configuration items are Auto Start, DNS Servers, Opus Bit Rate,
and ICE Lite
- Added ICE and Stun Server account options
- Fixed bug in deleting of an account
- Updated libbaresip.a and baresip.h to latest baresip master