added graphical account configuration and many other improvements

This commit is contained in:
Juha Heinanen
2018-05-23 10:58:57 +03:00
parent 784dd2feb5
commit f4bd1a93ed
27 changed files with 1635 additions and 418 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:25.2.0' implementation 'com.android.support:appcompat-v7:25.2.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.1' implementation 'com.android.support.constraint:constraint-layout:1.0.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
} }
repositories { repositories {
mavenCentral() mavenCentral()
+12 -2
View File
@@ -51,8 +51,18 @@
</activity> </activity>
<activity <activity
android:name=".EditAccountsActivity" android:name=".AccountsActivity"
android:label="Edit Accounts" android:label="Accounts"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.tutpro.baresip.MainActivity" />
</activity>
<activity
android:name=".AccountActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="Account"
android:parentActivityName=".MainActivity"> android:parentActivityName=".MainActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
-1
View File
@@ -31,4 +31,3 @@
# <sip:[email protected];auth_pass=secret;transport=tcp> # <sip:[email protected];auth_pass=secret;transport=tcp>
# <sip:user@[2001:df8:0:16:216:6fff:fe91:614c]:5070;transport=tcp;auth_pass=secret> # <sip:user@[2001:df8:0:16:216:6fff:fe91:614c]:5070;transport=tcp;auth_pass=secret>
# #
<sip:[email protected]>;auth_user=foo;auth_pass=xxx;outbound="sip:192.168.43.98:5060;transport=tcp";ptime=20;audio_codecs=OPUS/48000/2,PCMU,PCMA;video_codecs="";regint=600;pubint=0;sipnat=outbound
+3 -3
View File
@@ -128,9 +128,9 @@ module ice.so
#module natpmp.so #module natpmp.so
# Media encryption modules # Media encryption modules
#module srtp.so module srtp.so
#module dtls_srtp.so module dtls_srtp.so
#module zrtp.so module zrtp.so
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
+374 -13
View File
@@ -102,7 +102,12 @@ static void ua_event_handler(struct ua *ua, enum ua_event ev,
re_snprintf(event_buf, sizeof event_buf, "%s", "call incoming"); re_snprintf(event_buf, sizeof event_buf, "%s", "call incoming");
break; break;
case UA_EVENT_CALL_MENC: case UA_EVENT_CALL_MENC:
re_snprintf(event_buf, sizeof event_buf, "%s", prm); if (prm[0] == '1')
re_snprintf(event_buf, sizeof event_buf, "call verify,%s", prm+2);
else if (prm[0] == '2')
re_snprintf(event_buf, sizeof event_buf, "call verified,%s", prm+2);
else
re_snprintf(event_buf, sizeof event_buf, "%s", "unknown menc event");
break; break;
case UA_EVENT_CALL_CLOSED: case UA_EVENT_CALL_CLOSED:
re_snprintf(event_buf, sizeof event_buf, "%s", "call closed"); re_snprintf(event_buf, sizeof event_buf, "%s", "call closed");
@@ -317,26 +322,342 @@ Java_com_tutpro_baresip_MainActivity_baresipStop(JNIEnv *env, jobject thiz)
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_MainActivity_ua_1aor(JNIEnv *env, jobject thiz, jstring javaUA) Java_com_tutpro_baresip_AccountKt_account_1display_1name(JNIEnv *env, jobject thiz, jstring javaAcc) {
{ const char *native_acc = (*env)->GetStringUTFChars(env, javaAcc, 0);
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0); struct account *acc = (struct account *)strtoul(native_acc, NULL, 10);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
if (strlen(native_ua) > 0) (*env)->ReleaseStringUTFChars(env, javaAcc, native_acc);
return (*env)->NewStringUTF(env, ua_aor(ua)); if (acc) {
const char *dn = account_display_name(acc);
if (dn)
return (*env)->NewStringUTF(env, dn);
else
return (*env)->NewStringUTF(env, "");
} else
return (*env)->NewStringUTF(env, "");
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1display_1name(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaDn) {
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 *dn = (*env)->GetStringUTFChars(env, javaDn, 0);\
int res;
if (strlen(dn) > 0)
res = account_set_display_name(acc, dn);
else
res = account_set_display_name(acc, NULL);
(*env)->ReleaseStringUTFChars(env, javaDn, dn);
return res;
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_AccountKt_account_1aor(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)
return (*env)->NewStringUTF(env, account_aor(acc));
else else
return (*env)->NewStringUTF(env, ""); return (*env)->NewStringUTF(env, "");
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_MainActivity_ua_1mediaenc(JNIEnv *env, jobject thiz, jstring javaUA) Java_com_tutpro_baresip_AccountKt_account_1auth_1user(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 *au = account_auth_user(acc);
if (au)
return (*env)->NewStringUTF(env, au);
else
return (*env)->NewStringUTF(env, "");
} else
return (*env)->NewStringUTF(env, "");
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1auth_1user(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaUser) {
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 *user = (*env)->GetStringUTFChars(env, javaUser, 0);
int res;
if (strlen(user) > 0)
res = account_set_auth_user(acc, user);
else
res = account_set_auth_user(acc, NULL);
(*env)->ReleaseStringUTFChars(env, javaUser, user);
return res;
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_AccountKt_account_1auth_1pass(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 *ap = account_auth_pass(acc);
if (ap)
return (*env)->NewStringUTF(env, ap);
else
return (*env)->NewStringUTF(env, "");
} else
return (*env)->NewStringUTF(env, "");
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1auth_1pass(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaPass) {
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 *pass = (*env)->GetStringUTFChars(env, javaPass, 0);
int res;
if (strlen(pass) > 0)
res = account_set_auth_pass(acc, pass);
else
res = account_set_auth_pass(acc, NULL);
(*env)->ReleaseStringUTFChars(env, javaPass, pass);
return res;
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_AccountKt_account_1outbound(JNIEnv *env, jobject thiz, jstring javaAcc,
jint ix) {
const char *native_acc = (*env)->GetStringUTFChars(env, javaAcc, 0);
struct account *acc = (struct account *) strtoul(native_acc, NULL, 10);
const uint16_t native_ix = ix;
const char *outbound;
(*env)->ReleaseStringUTFChars(env, javaAcc, native_acc);
if (acc) {
outbound = account_outbound(acc, native_ix);
if (outbound)
return (*env)->NewStringUTF(env, outbound);
else
return (*env)->NewStringUTF(env, "");
} else {
return (*env)->NewStringUTF(env, "");
}
}
JNIEXPORT int JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1outbound(JNIEnv *env, jobject thiz, jstring javaAcc,
jstring javaOb, jint javaIx) {
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 *ob = (*env)->GetStringUTFChars(env, javaOb, 0);
const uint16_t ix = javaIx;
int res;
if (strlen(ob) > 0)
res = account_set_outbound(acc, ob, ix);
else
res = account_set_outbound(acc, NULL, ix);
(*env)->ReleaseStringUTFChars(env, javaOb, ob);
return res;
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_AccountKt_account_1audio_1codec(JNIEnv *env, jobject thiz, jstring javaAcc,
jint ix) {
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_ix = ix;
const struct list *codecl;
char codec_buf[32];
struct le *le;
if (acc) {
codecl = account_aucodecl(acc);
if (!list_isempty(codecl)) {
int i = -1;
for (le = list_head(codecl); le != NULL; le = le->next) {
i++;
if (i > ix) break;
if (i == ix) {
const struct aucodec *ac = le->data;
re_snprintf(codec_buf, sizeof codec_buf, "%s/%u/%u", ac->name, ac->srate, ac->ch);
break;
}
}
if (i == ix) {
return (*env)->NewStringUTF(env, codec_buf);
}
}
}
return (*env)->NewStringUTF(env, "");
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1audio_1codecs(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaCodecs) {
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 *codecs = (*env)->GetStringUTFChars(env, javaCodecs, 0);
LOGD("setting audio codecs '%s'\n", codecs);
int res = account_set_audio_codecs(acc, codecs);
(*env)->ReleaseStringUTFChars(env, javaCodecs, codecs);
return res;
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1regint(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)
return account_regint(acc);
else
return 0;
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1regint(JNIEnv *env, jobject thiz,
jstring javaAcc, jint javaRegint) {
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 uint32_t regint = javaRegint;
return account_set_regint(acc, regint);
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_AccountKt_account_1mediaenc(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 *mediaenc = account_mediaenc(acc);
if (mediaenc) return (*env)->NewStringUTF(env, mediaenc);
}
return (*env)->NewStringUTF(env, "");
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1mediaenc(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaMencid) {
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 *mencid = (*env)->GetStringUTFChars(env, javaMencid, 0);
int res;
if (strlen(mencid) > 0)
res = account_set_mediaenc(acc, mencid);
else
res = account_set_mediaenc(acc, NULL);
(*env)->ReleaseStringUTFChars(env, javaMencid, mencid);
return res;
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1alloc(JNIEnv *env, jobject thiz,
jstring javaSipUri) {
const char *sip_uri = (*env)->GetStringUTFChars(env, javaSipUri, 0);
struct ua *ua;
LOGD("allocating UA '%s'\n", sip_uri);
int res = ua_alloc(&ua, sip_uri);
(*env)->ReleaseStringUTFChars(env, javaSipUri, sip_uri);
if (res == 0) {
char ua_buf[64];
UpdateContext *pctx = (UpdateContext*)(&g_ctx);
sprintf(ua_buf, "%lu", (unsigned long)ua);
jstring javaUA = (*env)->NewStringUTF(env, ua_buf);
LOGD("adding account %s/%s\n", ua_aor(ua), ua_buf);
jmethodID accountId = (*env)->GetMethodID(env, pctx->mainActivityClz, "addAccount",
"(Ljava/lang/String;)V");
(*env)->CallVoidMethod(env, pctx->mainActivityObj, accountId, javaUA);
(*env)->DeleteLocalRef(env, javaUA);
}
return res;
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1register(JNIEnv *env, jobject thiz,
jstring javaUA)
{ {
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0); const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10); struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
struct account *acc = ua_account(ua); (*env)->ReleaseStringUTFChars(env, javaUA, native_ua);
char *mediaenc = account_mediaenc(acc); return ua_register(ua);
if (mediaenc) }
return (*env)->NewStringUTF(env, mediaenc);
JNIEXPORT void JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1unregister(JNIEnv *env, jobject thiz,
jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaUA, native_ua);
ua_unregister(ua);
}
JNIEXPORT jboolean JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1isregistered(JNIEnv *env, jobject thiz,
jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaUA, native_ua);
return ua_isregistered(ua);
}
JNIEXPORT void JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1destroy(JNIEnv *env, jobject thiz,
jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaUA, native_ua);
mem_deref(ua);
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_UserAgentKt_ua_1account(JNIEnv *env, jobject thiz, jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
struct account *acc;
char acc_buf[256];
(*env)->ReleaseStringUTFChars(env, javaUA, native_ua);
acc_buf[0] = '\0';
if (ua) {
acc = ua_account(ua);
if (acc) sprintf(acc_buf, "%lu", (unsigned long) acc);
}
return (*env)->NewStringUTF(env, acc_buf);
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_UserAgentKt_ua_1aor(JNIEnv *env, jobject thiz, jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaUA, native_ua);
if (ua)
return (*env)->NewStringUTF(env, ua_aor(ua));
else else
return (*env)->NewStringUTF(env, ""); return (*env)->NewStringUTF(env, "");
} }
@@ -514,10 +835,50 @@ Java_com_tutpro_baresip_MainActivity_reload_1config(JNIEnv *env, jobject thiz) {
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_MainActivity_cmd_1exec(JNIEnv *env, jobject thiz, jstring javaCmd) { Java_com_tutpro_baresip_Utils_cmd_1exec(JNIEnv *env, jobject thiz, jstring javaCmd) {
const char *native_cmd = (*env)->GetStringUTFChars(env, javaCmd, 0); const char *native_cmd = (*env)->GetStringUTFChars(env, javaCmd, 0);
LOGD("processing command '%s'\n", native_cmd); LOGD("processing command '%s'\n", native_cmd);
int res = cmd_process_long(baresip_commands(), native_cmd, strlen(native_cmd), &pf_null, NULL); int res = cmd_process_long(baresip_commands(), native_cmd, strlen(native_cmd), &pf_null, NULL);
(*env)->ReleaseStringUTFChars(env, javaCmd, native_cmd); (*env)->ReleaseStringUTFChars(env, javaCmd, native_cmd);
return res; return res;
} }
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_Utils_audio_1codecs(JNIEnv *env, jobject thiz)
{
struct list *aucodecl = baresip_aucodecl();
struct le *le;
char codec_buf[256];
char *start = &(codec_buf[0]);
unsigned int left = sizeof codec_buf;
int len;
for (le = list_head(aucodecl); le != NULL; le = le->next) {
const struct aucodec *ac = le->data;
if (start == &(codec_buf[0]))
len = re_snprintf(start, left, "%s/%u/%u", ac->name, ac->srate, ac->ch);
else
len = re_snprintf(start, left, ",%s/%u/%u", ac->name, ac->srate, ac->ch);
if (len == -1) {
LOGE("failed to print codec to buffer\n");
codec_buf[0] = '\0';
break;
}
start = start + len;
left = left - len;
}
return (*env)->NewStringUTF(env, codec_buf);
}
JNIEXPORT jboolean JNICALL
Java_com_tutpro_baresip_Utils_uri_1decode(JNIEnv *env, jobject thiz, jstring javaUri) {
const char *uri = (*env)->GetStringUTFChars(env, javaUri, 0);
struct pl pl;
struct uri uri2;
LOGD("decoding uri '%s'\n", uri);
pl_set_str(&pl, uri);
int res = uri_decode(&uri2, &pl);
(*env)->ReleaseStringUTFChars(env, javaUri, uri);
return res == 0;
}
@@ -1,3 +1,128 @@
package com.tutpro.baresip package com.tutpro.baresip
class Account(val ua: String, val aoR: String, val mediaenc: String, var status: String) {} class Account(val accp: String) {
var displayName = account_display_name(accp)
val aor = account_aor(accp)
var authUser = account_auth_user(accp)
var authPass = account_auth_pass(accp)
var outbound = ArrayList<String>()
var audioCodec = ArrayList<String>()
var regint = account_regint(accp)
var mediaenc = account_mediaenc(accp)
init {
var i = 0
while (true) {
val ob = account_outbound(accp, i)
if (ob != "") {
outbound.add(ob)
i++
} else {
break
}
}
i = 0
while (true) {
val ac = account_audio_codec(accp, i)
if (ac != "") {
audioCodec.add(ac)
i++
} else {
break
}
}
}
fun print() : String {
var res: String
if (displayName != "")
res = "\"${displayName}\" "
else
res = ""
res = res + "<${aor}>"
if (authUser != "") res = res + ";auth_user=\"${authUser}\""
if (authPass != "") res = res + ";auth_pass=\"${authPass}\""
if (outbound.size > 0) res = res + ";outbound=\"${outbound[0]}\""
if (outbound.size > 1) res = res + ";outbound2=\"${outbound[1]}\""
if (audioCodec.size > 0) {
var first = true
res = res + ";audio_codecs="
for (c in audioCodec)
if (first) {
res = res + c
first = false
} else {
res = res + ",$c"
}
}
if (mediaenc != "") res = res + ";mediaenc=${mediaenc}"
res = res + ";ptime=20;regint=${regint};regq=0.5;pubint=0;answermode=manual;sipnat=outbound"
return res
}
companion object {
fun accounts(): ArrayList<Account> {
var res = ArrayList<Account>()
for (ua in MainActivity.uas) {
res.add(ua.account)
}
return res
}
fun find(uas: ArrayList<UserAgent>, accp: String): Account? {
for (ua in uas) {
if (ua.account.accp == accp) return ua.account
}
return null
}
fun findUA(aor: String): UserAgent? {
for (ua in MainActivity.uas) {
if (ua.aor == aor) return ua
}
return null
}
fun exists(uas: ArrayList<UserAgent>, aor: String): Boolean {
for (ua in uas) {
if (ua.account.aor == aor) return true
}
return false
}
fun printNew(aor: String) : String {
var res = "<$aor>;auth_user=\"\";auth_pass=\"\";audio_codecs=opus/48000/2,PCMU,PCMA"
res = res + ";ptime=20;regint=600;regq=0.5;pubint=0;answermode=manual;sipnat=outbound"
res = res + ";mediaenc=zrtp"
return res
}
}
}
external fun account_set_display_name(acc: String, dn: String): Int
external fun account_display_name(acc: String): String
external fun account_aor(acc: String): String
external fun account_auth_user(acc: String): String
external fun account_set_auth_user(acc: String, user: String): Int
external fun account_auth_pass(acc: String): String
external fun account_set_auth_pass(acc: String, pass: String): Int
external fun account_outbound(acc: String, ix: Int): String
external fun account_set_outbound(acc: String, ob: String, ix: Int): 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_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
@@ -0,0 +1,298 @@
package com.tutpro.baresip
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.LinearLayout.LayoutParams
import android.widget.*
class AccountActivity : AppCompatActivity() {
internal lateinit var acc: Account
internal lateinit var displayName: EditText
internal lateinit var aor: String
internal lateinit var authUser: EditText
internal lateinit var authPass: EditText
internal lateinit var outbound1: EditText
internal lateinit var outbound2: EditText
internal lateinit var regint: EditText
internal lateinit var mediaEnc: String
private var newCodecs = ArrayList<String>()
private var save = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_account)
val b = intent.extras
acc = Account.find(MainActivity.uas, b.getString("accp"))!!
aor = acc.aor
setTitle(aor.replace("sip:", ""))
displayName = findViewById(R.id.DisplayName) as EditText
displayName.setText(acc.displayName)
authUser = findViewById(R.id.AuthUser) as EditText
authUser.setText(acc.authUser)
authPass = findViewById(R.id.AuthPass) as EditText
authPass.setText(acc.authPass)
outbound1 = findViewById(R.id.Outbound1) as EditText
outbound2 = findViewById(R.id.Outbound2) as EditText
if (acc.outbound.size > 0) {
outbound1.setText(acc.outbound[0])
if (acc.outbound.size > 1)
outbound2.setText(acc.outbound[1])
}
regint = findViewById(R.id.RegInt) as EditText
regint.setText(acc.regint.toString())
val audioCodecs = ArrayList(Utils.audio_codecs().split(","))
newCodecs.addAll(audioCodecs)
while (newCodecs.size < audioCodecs.size) newCodecs.add("")
val layout = findViewById(R.id.CodecSpinners) as LinearLayout
for (i in audioCodecs.indices) {
val spinner = Spinner(applicationContext)
spinner.id = i + 100
spinner.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
spinner.setPopupBackgroundResource(R.drawable.spinner_background)
layout.addView(spinner)
var spinnerList = ArrayList<String>()
if (acc.audioCodec.size > i) {
val codec = acc.audioCodec[i]
spinnerList.add(codec)
for (c in audioCodecs) if (c != codec) spinnerList.add(c)
spinnerList.add("")
} else {
spinnerList = audioCodecs
spinnerList.add(0, "")
}
val codecSpinner = findViewById(spinner.id) as Spinner
val adapter = ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,
spinnerList)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
codecSpinner.adapter = adapter
codecSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
newCodecs.set(parent.id - 100, parent.selectedItem.toString())
}
override fun onNothingSelected(parent: AdapterView<*>) {
}
}
}
mediaEnc = acc.mediaenc
val mediaEncSpinner = findViewById(R.id.mediaEncSpinner) as Spinner
val mediaEncKeys = arrayListOf("zrtp", "dtls_srtp", "srtp", "srtp-mand", "")
val mediaEncVals = arrayListOf("ZRTP", "DTLS SRTP", "SRTP", "Madatory SRTP", "")
val keyIx = mediaEncKeys.indexOf(acc.mediaenc)
val keyVal = mediaEncVals.elementAt(keyIx)
mediaEncKeys.removeAt(keyIx)
mediaEncVals.removeAt(keyIx)
mediaEncKeys.add(0, acc.mediaenc)
mediaEncVals.add(0, keyVal)
val mediaEncAdapter = ArrayAdapter<String>(this,android.R.layout.simple_spinner_item,
mediaEncVals)
mediaEncAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
mediaEncSpinner.adapter = mediaEncAdapter
mediaEncSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
mediaEnc = mediaEncKeys[mediaEncVals.indexOf(parent.selectedItem.toString())]
}
override fun onNothingSelected(parent: AdapterView<*>) {
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
val inflater = menuInflater
inflater.inflate(R.menu.edit_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val intent = Intent(this, MainActivity::class.java)
when (item.itemId) {
R.id.save -> {
val dn = displayName.text.toString().trim()
if (dn != acc.displayName) {
if (checkDisplayName(dn)) {
if (account_set_display_name(acc.accp, dn) == 0) {
acc.displayName = account_display_name(acc.accp);
Log.d("Baresip", "New display name is ${acc.displayName}")
save = true
} else {
Log.e("Baresip", "Setting of display name failed")
}
} else {
Utils.alertView(this, "Notice", "Invalid Display Name: $dn")
return false
}
}
val au = authUser.text.toString().trim()
if (au != acc.authUser) {
if (checkAuthUser(au)) {
if (account_set_auth_user(acc.accp, au) == 0) {
acc.authUser = account_auth_user(acc.accp);
Log.d("Baresip", "New auth user is ${acc.authUser}")
save = true
} else {
Log.e("Baresip", "Setting of auth user failed")
}
} else {
Utils.alertView(this, "Notice",
"Invalid Authentication UserName: $au")
return false
}
}
val ap = authPass.text.toString().trim()
if (ap != acc.authPass) {
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}")
save = true
} else {
Log.e("Baresip", "Setting of auth pass failed")
}
} else {
Utils.alertView(this, "Notice",
"Invalid Authentication Password: $ap")
return false
}
}
val ob = ArrayList<String>()
if (outbound1.text.toString().trim() != "")
ob.add(outbound1.text.toString().trim().replace(" ", ""))
if (outbound2.text.toString().trim() != "")
ob.add(outbound2.text.toString().trim().replace(" ", ""))
if (ob != acc.outbound) {
val outbound = ArrayList<String>()
for (i in ob.indices) {
if ((ob[i] == "") || Utils.uri_decode(ob[i])) {
if (account_set_outbound(acc.accp, ob[i], i) == 0) {
if (ob[i] != "")
outbound.add(account_outbound(acc.accp, i))
} else {
Log.e("Baresip", "Setting of outbound proxy ${ob[i]} failed")
break
}
} else {
Utils.alertView(this, "Notice",
"Invalid Outbound Proxy: ${ob[i]}")
return false
}
}
Log.d("Baresip", "New outbound proxies are ${outbound}")
acc.outbound = outbound
save = true
}
val ri = regint.text.toString().trim()
if (Utils.checkUint(ri)) {
if (ri.toInt() != acc.regint) {
if (account_set_regint(acc.accp, ri.toInt()) == 0) {
acc.regint = account_regint(acc.accp)
Log.d("Baresip", "New regint is ${acc.regint}")
save = true
} else {
Log.e("Baresip", "Setting of regint $ri failed")
}
}
} else {
Utils.alertView(this, "Notice",
"Invalid Registration Interval: $ri")
return false
}
val ac = ArrayList(LinkedHashSet<String>(newCodecs.filter{it != ""} as ArrayList<String>))
if (ac != acc.audioCodec) {
Log.d("Baresip", "New codecs ${newCodecs.filter { it != "" }}")
val acParam = ";audio_codecs=" + Utils.implode(ac, ",")
if (account_set_audio_codecs(acc.accp, acParam) == 0) {
var i = 0
while (true) {
val codec = account_audio_codec(acc.accp, i)
if (codec != "") {
Log.d("Baresip", "Found audio codec $codec")
i++
} else {
break
}
}
acc.audioCodec = ac
save = true
} else {
Log.e("Baresip", "Setting of audio codecs $acParam failed")
}
}
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}")
save = true
} else {
Log.e("Baresip", "Setting of mediaenc $mediaEnc failed")
}
}
if (save) {
AccountsActivity.saveAccounts()
if (acc.regint > 0) {
Log.d("Baresip", "Registering ${acc.aor}")
UserAgent.ua_register(Account.findUA(acc.aor)!!.uap)
}
}
setResult(RESULT_OK, intent)
finish()
return true
}
R.id.cancel, android.R.id.home -> {
intent.putExtra("action", "cancel")
if (acc.regint > 0) {
Log.d("Baresip", "Registering ${acc.aor}")
UserAgent.ua_register(Account.findUA(acc.aor)!!.uap)
}
setResult(RESULT_OK, intent)
finish()
return true
}
else -> return super.onOptionsItemSelected(item)
}
}
private fun checkDisplayName(dn: String): Boolean {
if (dn == "") return true
val dnRegex = Regex("^[a-zA-Z]([ ._-]|[a-zA-Z0-9]){1,63}\$")
return dnRegex.matches(dn)
}
private fun checkAuthUser(au: String): Boolean {
if (au == "") return true
val ud = au.split("@")
val userIDRegex = Regex("^[a-zA-Z]([._-]|[a-zA-Z0-9]){1,63}\$")
val telnoRegex = Regex("^[+]?[0-9]{1,16}\$")
if (ud.size == 1) {
return userIDRegex.matches(ud[0]) || telnoRegex.matches(ud[0])
} else {
return (userIDRegex.matches(ud[0]) || telnoRegex.matches(ud[0])) &&
Utils.checkDomain(ud[1])
}
}
}
@@ -0,0 +1,61 @@
package com.tutpro.baresip
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageButton
import android.widget.TextView
import java.util.*
class AccountListAdapter(private val cxt: Context, private val rows: ArrayList<AccountRow>) :
ArrayAdapter<AccountRow>(cxt, R.layout.account_row, rows) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val row = rows[position]
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val rowView = inflater.inflate(R.layout.account_row, parent, false)
val aorView = rowView.findViewById(R.id.aor) as TextView
aorView.text = row.aor
aorView.textSize = 20f
aorView.setPadding(6, 6, 0, 6)
aorView.setOnClickListener { _ ->
val i = Intent(cxt, AccountActivity::class.java)
val b = Bundle()
b.putString("accp", MainActivity.uas[position].account.accp)
i.putExtras(b)
cxt.startActivity(i)
}
val actionView = rowView.findViewById(R.id.action) as ImageButton
actionView.setImageResource(row.action)
actionView.setOnClickListener { _ ->
Log.d("Baresip", "Delete button clicked")
val deleteDialog = AlertDialog.Builder(cxt)
deleteDialog.setMessage("Do you want to delete account ${MainActivity.uas[position].aor}?")
deleteDialog.setPositiveButton("Delete") { dialog, _ ->
if (UserAgent.ua_isregistered(MainActivity.uas[position].uap)) {
UserAgent.ua_unregister(MainActivity.uas[position].uap)
}
UserAgent.ua_destroy(MainActivity.uas[position].uap)
MainActivity.uas.remove(MainActivity.uas[position])
MainActivity.images.removeAt(position)
AccountsActivity.saveAccounts()
AccountsActivity.generateAccounts()
this.notifyDataSetChanged()
dialog.dismiss()
}
deleteDialog.setNegativeButton("No") { dialog, _ ->
dialog.dismiss()
}
deleteDialog.create().show()
}
return rowView
}
}
@@ -0,0 +1,3 @@
package com.tutpro.baresip
class AccountRow(val aor: String, val action: Int)
@@ -0,0 +1,100 @@
package com.tutpro.baresip
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.SystemClock
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.MenuItem
import android.widget.*
import java.io.File
import java.util.ArrayList
class AccountsActivity : AppCompatActivity() {
internal var aor: String = ""
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_accounts)
val listview = findViewById(R.id.accounts) as ListView
generateAccounts()
val alAdapter = AccountListAdapter(this, accounts)
listview.adapter = alAdapter
val addAccountButton = findViewById(R.id.addAccount) as ImageButton
val newAorView = findViewById(R.id.newAor) as EditText
addAccountButton.setOnClickListener{
var aor = newAorView.text.toString()
if (!aor.startsWith("sip:")) aor = "sip:$aor"
if (!checkSipUri(aor)) {
Log.e("Baresip", "Invalid SIP URI $aor")
Utils.alertView(this, "Notice",
"Invalid SIP URI: $aor")
} else if (Account.exists(MainActivity.uas, aor)) {
Log.e("Baresip", "Account $aor already exists")
} else {
if (UserAgent.ua_alloc("<$aor>;regq=0.5;pubint=0;regint=600") != 0)
Log.e("Baresip", "Failed to allocate UA $aor")
else {
SystemClock.sleep(200);
newAorView.setText("")
newAorView.setHint("SIP URI user@domain")
newAorView.clearFocus()
saveAccounts()
recreate()
}
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
Log.d("Baresip", "Back array was pressed at Accounts")
val i = Intent()
setResult(Activity.RESULT_CANCELED, i)
finish()
}
}
return true
}
private fun checkSipUri(uri: String): Boolean {
if (!uri.startsWith("sip:")) return false
val userDomain = uri.replace("sip:", "").split("@")
if (userDomain.size != 2) return false
if (!Utils.checkUserID(userDomain[0]) && !Utils.checkTelNo(userDomain[0]))
return false
return Utils.checkDomain(userDomain[1]) || Utils.checkIP(userDomain[1])
}
companion object {
var accounts = ArrayList<AccountRow>()
var posAtAccounts = ArrayList<Int>()
fun generateAccounts() {
accounts.clear()
posAtAccounts.clear()
var i = 0;
for (ua in MainActivity.uas) {
accounts.add(AccountRow(ua.aor.replace("sip:", ""), R.drawable.action_remove))
posAtAccounts.add(i)
i++
}
}
fun saveAccounts() {
var accounts = ""
for (a in Account.accounts()) accounts = accounts + a.print() + "\n"
val path = MainActivity.filesPath + "/accounts"
Utils.putFileContents(File(path), accounts)
Log.d("Baresip", "Saved accounts '${accounts}' to '$path")
}
}
}
+27 -11
View File
@@ -3,31 +3,47 @@ package com.tutpro.baresip
import android.text.TextWatcher import android.text.TextWatcher
import java.util.ArrayList import java.util.ArrayList
class Call(val ua: String, val call: String, val peerURI: String, var status: String, class Call(val callp: String, val ua: UserAgent, val peerURI: String, val dir: String,
val dtmfWatcher: TextWatcher?) { var status: String, val dtmfWatcher: TextWatcher?) {
var hold: Boolean = false var hold = false
var security: Int = 0 var security = 0
var zid: String = "" var zid = ""
var hasHistory = false
companion object { companion object {
fun uaCalls(calls: ArrayList<Call>, ua: String): ArrayList<Call> { fun calls(calls: ArrayList<Call>, dir: String): ArrayList<Call> {
val result = ArrayList<Call>() val result = ArrayList<Call>()
for (i in calls.indices) { for (i in calls.indices) {
if (calls[i].ua == ua) result.add(calls[i]) if (calls[i].dir == dir) result.add(calls[i])
} }
return result return result
} }
fun callIndex(calls: ArrayList<Call>, ua: String, call: String): Int { fun uaCalls(calls: ArrayList<Call>, ua: UserAgent, dir: String): ArrayList<Call> {
val result = ArrayList<Call>()
for (i in calls.indices) { for (i in calls.indices) {
if (calls[i].ua.equals(ua) && calls[i].call == call) if ((calls[i].ua == ua) && (calls[i].dir == dir)) result.add(calls[i])
return i
} }
return -1 return result
} }
fun find(calls: ArrayList<Call>, callp: String): Call? {
for (c in calls) {
if (c.callp == callp) return c
}
return null
}
fun index(calls: ArrayList<Call>, call: Call, dir: String): Int {
var res = 0
for (c in calls) {
if (c == call) return res
if ((dir == "") || (call.dir == dir)) res++
}
return res
}
} }
} }
@@ -0,0 +1,3 @@
package com.tutpro.baresip
class Codec(val name: String, val rate: Int, val channels: Int) {}
@@ -1,103 +0,0 @@
package com.tutpro.baresip
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.MenuInflater
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 EditAccountsActivity : 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_accounts)
editText = findViewById(R.id.editAccounts) as EditText
path = applicationContext.filesDir.absolutePath + "/accounts"
file = File(path!!)
var content: String
if (!file!!.exists()) {
Log.e("Baresip", "Failed to find accounts file")
content = "No accounts"
} else {
Log.e("Baresip", "Found accounts 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 accounts 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.edit_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val i = Intent()
when (item.itemId) {
R.id.save -> {
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 accounts file: " + e.toString())
}
} catch (e: java.io.FileNotFoundException) {
Log.e("Baresip", "Failed to find accounts file: " + e.toString())
}
Log.d("Baresip", "Updated accounts file")
setResult(RESULT_OK, i)
finish()
return true
}
R.id.cancel, android.R.id.home -> {
setResult(RESULT_CANCELED, i)
finish()
return true
}
else -> return super.onOptionsItemSelected(item)
}
}
}
@@ -3,8 +3,8 @@ package com.tutpro.baresip
import java.io.Serializable import java.io.Serializable
import java.util.GregorianCalendar import java.util.GregorianCalendar
class History(val ua: String, val call: String, val aor: String, val peerURI: String, class History(val aor: String, val peerURI: String, val direction: String,
val direction: String, val connected: Boolean) : Serializable { val connected: Boolean) : Serializable {
val time: GregorianCalendar = GregorianCalendar() val time: GregorianCalendar = GregorianCalendar()
@@ -30,7 +30,7 @@ class HistoryActivity : AppCompatActivity() {
val b = intent.extras val b = intent.extras
aor = b.getString("aor") aor = b.getString("aor")
generate_ua_history(aor) aorGenerateHistory(aor)
val adapter = HistoryListAdapter(this, uaHistory) val adapter = HistoryListAdapter(this, uaHistory)
listview.adapter = adapter listview.adapter = adapter
@@ -46,7 +46,7 @@ class HistoryActivity : AppCompatActivity() {
when (which) { when (which) {
DialogInterface.BUTTON_POSITIVE -> { DialogInterface.BUTTON_POSITIVE -> {
History.removeAt(posAtHistory[pos]) History.removeAt(posAtHistory[pos])
generate_ua_history(aor) aorGenerateHistory(aor)
if (uaHistory.size == 0) { if (uaHistory.size == 0) {
val i = Intent() val i = Intent()
setResult(Activity.RESULT_CANCELED, i) setResult(Activity.RESULT_CANCELED, i)
@@ -82,7 +82,7 @@ class HistoryActivity : AppCompatActivity() {
return true return true
} }
private fun generate_ua_history(aor: String) { private fun aorGenerateHistory(aor: String) {
uaHistory.clear() uaHistory.clear()
posAtHistory.clear() posAtHistory.clear()
for (i in History.indices.reversed()) { for (i in History.indices.reversed()) {
@@ -137,13 +137,6 @@ class HistoryActivity : AppCompatActivity() {
return size return size
} }
fun callHasHistory(ua: String, call: String): Boolean {
for (h in History) {
if (h.ua == ua && h.call == call) return true
}
return false
}
fun aorRemoveHistory(aor: String) { fun aorRemoveHistory(aor: String) {
for (h in History) { for (h in History) {
if (h.aor == aor) { if (h.aor == aor) {
@@ -1,7 +1,6 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.Manifest import android.Manifest
import android.app.Notification
import android.app.Notification.VISIBILITY_PUBLIC import android.app.Notification.VISIBILITY_PUBLIC
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
@@ -35,14 +34,14 @@ import java.io.*
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
internal lateinit var appContext: Context lateinit var appContext: Context
internal lateinit var layout: RelativeLayout internal lateinit var layout: RelativeLayout
internal lateinit var callee: AutoCompleteTextView internal lateinit var callee: AutoCompleteTextView
internal lateinit var securityButton: ImageButton internal lateinit var securityButton: ImageButton
internal lateinit var callButton: Button internal lateinit var callButton: Button
internal lateinit var holdButton: Button internal lateinit var holdButton: Button
internal lateinit var dtmf: EditText internal lateinit var dtmf: EditText
internal lateinit var accountAdapter: AccountSpinnerAdapter internal lateinit var uaAdapter: UaSpinnerAdapter
internal lateinit var aorSpinner: Spinner internal lateinit var aorSpinner: Spinner
internal lateinit var am: AudioManager internal lateinit var am: AudioManager
internal lateinit var imm: InputMethodManager internal lateinit var imm: InputMethodManager
@@ -61,6 +60,7 @@ class MainActivity : AppCompatActivity() {
callButton = findViewById(R.id.callButton) as Button callButton = findViewById(R.id.callButton) as Button
holdButton = findViewById(R.id.holdButton) as Button holdButton = findViewById(R.id.holdButton) as Button
dtmf = findViewById(R.id.dtmf) as EditText dtmf = findViewById(R.id.dtmf) as EditText
filesPath = applicationContext.filesDir.absolutePath
am = getSystemService(Context.AUDIO_SERVICE) as AudioManager am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
@@ -78,15 +78,16 @@ class MainActivity : AppCompatActivity() {
.setContent(RemoteViews(getPackageName(), R.layout.notification)) .setContent(RemoteViews(getPackageName(), R.layout.notification))
aorSpinner = findViewById(R.id.AoRList) as Spinner aorSpinner = findViewById(R.id.AoRList) as Spinner
accountAdapter = AccountSpinnerAdapter(applicationContext, accounts, images) uaAdapter = UaSpinnerAdapter(applicationContext, uas, images)
aorSpinner.adapter = accountAdapter aorSpinner.adapter = uaAdapter
aorSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { aorSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
val aor = accounts[position].aoR val acc = uas[position].account
val ua = accounts[position].ua val aor = acc.aor
val ua = uas[position]
Log.i("Baresip", "Setting $aor current") Log.i("Baresip", "Setting $aor current")
val out = Call.uaCalls(callsOut, ua) val callsOut = Call.uaCalls(calls, ua, "out")
if (out.size == 0) { if (callsOut.size == 0) {
callee.text.clear() callee.text.clear()
callee.hint = "Callee" callee.hint = "Callee"
callButton.text = "Call" callButton.text = "Call"
@@ -99,18 +100,18 @@ class MainActivity : AppCompatActivity() {
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
dtmf.visibility = View.INVISIBLE dtmf.visibility = View.INVISIBLE
} else { } else {
callee.setText(out[0].peerURI) callee.setText(callsOut[0].peerURI)
callButton.text = out[0].status callButton.text = callsOut[0].status
if (out[0].status == "Hangup") { if (callsOut[0].status == "Hangup") {
if (out[0].hold) { if (callsOut[0].hold) {
holdButton.text = "Unhold" holdButton.text = "Unhold"
} else { } else {
holdButton.text = "Hold" holdButton.text = "Hold"
} }
holdButton.visibility = View.VISIBLE holdButton.visibility = View.VISIBLE
securityButton.setImageResource(out[0].security) securityButton.setImageResource(callsOut[0].security)
Utils.setSecurityButtonTag(securityButton, out[0].security) setSecurityButtonTag(securityButton, callsOut[0].security)
if (ua_mediaenc(ua) == "") if (acc.mediaenc == "")
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
else else
securityButton.visibility = View.VISIBLE securityButton.visibility = View.VISIBLE
@@ -122,7 +123,7 @@ class MainActivity : AppCompatActivity() {
dtmf.visibility = View.INVISIBLE dtmf.visibility = View.INVISIBLE
} }
} }
val `in` = Call.uaCalls(callsIn, ua) val `in` = Call.uaCalls(calls, ua, "in")
val view_count = layout.childCount val view_count = layout.childCount
Log.d("Baresip", "View count is $view_count") Log.d("Baresip", "View count is $view_count")
if (view_count > 7) { if (view_count > 7) {
@@ -130,7 +131,7 @@ class MainActivity : AppCompatActivity() {
layout.removeViews(7, view_count - 7) layout.removeViews(7, view_count - 7)
} }
for (c in `in`) { for (c in `in`) {
for (call_index in callsIn.indices) { for (call_index in Call.calls(calls, "in").indices) {
addCallViews(ua, c, (call_index + 1) * 10) addCallViews(ua, c, (call_index + 1) * 10)
} }
} }
@@ -141,6 +142,23 @@ class MainActivity : AppCompatActivity() {
} }
} }
aorSpinner.setOnTouchListener { view, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
if ((event.x - view.left) < 100) {
val i = Intent(this, AccountActivity::class.java)
val b = Bundle()
b.putString("accp", uas[aorSpinner.selectedItemPosition].account.accp)
i.putExtras(b)
startActivityForResult(i, ACCOUNT_CODE)
true
} else {
false
}
} else {
false
}
}
val assets = arrayOf("accounts", "contacts", "config", "busy.wav", "callwaiting.wav", val assets = arrayOf("accounts", "contacts", "config", "busy.wav", "callwaiting.wav",
"error.wav", "message.wav", "notfound.wav", "ring.wav", "ringback.wav") "error.wav", "message.wav", "notfound.wav", "ring.wav", "ringback.wav")
val path = applicationContext.filesDir.path val path = applicationContext.filesDir.path
@@ -159,7 +177,7 @@ class MainActivity : AppCompatActivity() {
file = File("$path/$a") file = File("$path/$a")
if (!file.exists()) { if (!file.exists()) {
Log.d("Baresip", "Copying asset $a") Log.d("Baresip", "Copying asset $a")
Utils.copyAssetToFile(applicationContext, a, "$path/$a") copyAssetToFile(applicationContext, a, "$path/$a")
} else { } else {
Log.d("Baresip", "Asset $a already copied") Log.d("Baresip", "Asset $a already copied")
} }
@@ -207,11 +225,11 @@ class MainActivity : AppCompatActivity() {
unverifyDialog.setMessage("This call is SECURE and peer is VERIFIED! " + unverifyDialog.setMessage("This call is SECURE and peer is VERIFIED! " +
"Do you want to unverify the peer?") "Do you want to unverify the peer?")
unverifyDialog.setPositiveButton("Unverify") { dialog, _ -> unverifyDialog.setPositiveButton("Unverify") { dialog, _ ->
val out = Call.uaCalls(callsOut, accounts[aorSpinner.selectedItemPosition].ua) val outCalls = Call.uaCalls(calls, uas[aorSpinner.selectedItemPosition], "out")
if (out.size > 0) { if (outCalls.size > 0) {
if (cmd_exec("zrtp_unverify " + out[0].zid) != 0) { if (Utils.cmd_exec("zrtp_unverify " + outCalls[0].zid) != 0) {
Log.w("Baresip", Log.w("Baresip",
"Command 'zrtp_unverify ${out[0].zid}' failed") "Command 'zrtp_unverify ${outCalls[0].zid}' failed")
} else { } else {
securityButton.setImageResource(R.drawable.box_yellow) securityButton.setImageResource(R.drawable.box_yellow)
securityButton.tag = "yellow" securityButton.tag = "yellow"
@@ -229,8 +247,8 @@ class MainActivity : AppCompatActivity() {
callButton.text = "Call" callButton.text = "Call"
callButton.setOnClickListener { callButton.setOnClickListener {
val aor = accounts[aorSpinner.selectedItemPosition].aoR val ua = uas[aorSpinner.selectedItemPosition]
val ua = accounts[aorSpinner.selectedItemPosition].ua val aor = ua.aor
when (callButton.text.toString()) { when (callButton.text.toString()) {
"Call" -> { "Call" -> {
val callee = (findViewById(R.id.callee) as EditText).text.toString() val callee = (findViewById(R.id.callee) as EditText).text.toString()
@@ -245,14 +263,16 @@ class MainActivity : AppCompatActivity() {
} }
} }
"Cancel" -> { "Cancel" -> {
Log.i("Baresip", "Canceling AoR $aor call " + val callp = Call.calls(calls, "out")[0].callp
callsOut[0].call + " to " + (findViewById(R.id.callee) as EditText).text) Log.i("Baresip", "Canceling AoR $aor call $callp to " +
ua_hangup(ua, callsOut[0].call, 486, "Rejected") (findViewById(R.id.callee) as EditText).text)
ua_hangup(ua.uap, callp, 486, "Rejected")
} }
"Hangup" -> { "Hangup" -> {
Log.i("Baresip", "Hanging up AoR $aor call " + val callp = Call.calls(calls, "out")[0].callp
callsOut[0].call + " to " + (findViewById(R.id.callee) as EditText).text) Log.i("Baresip", "Hanging up AoR $aor call $callp to " +
ua_hangup(ua, callsOut[0].call,0, "") (findViewById(R.id.callee) as EditText).text)
ua_hangup(ua.uap, callp,0, "")
} }
} }
} }
@@ -262,20 +282,22 @@ class MainActivity : AppCompatActivity() {
when (holdButton.text.toString()) { when (holdButton.text.toString()) {
"Hold" -> { "Hold" -> {
Log.i("Baresip", "Holding up " + (findViewById(R.id.callee) as EditText).text) Log.i("Baresip", "Holding up " + (findViewById(R.id.callee) as EditText).text)
call_hold(callsOut[0].call) val call = Call.calls(calls, "out")[0]
callsOut[0].hold = true call_hold(call.callp)
call.hold = true
holdButton.text = "Unhold" holdButton.text = "Unhold"
} }
"Unhold" -> { "Unhold" -> {
Log.i("Baresip", "Unholding " + (findViewById(R.id.callee) as EditText).text) Log.i("Baresip", "Unholding " + (findViewById(R.id.callee) as EditText).text)
call_unhold(callsOut[0].call) val call = Call.calls(calls, "out")[0]
callsOut[0].hold = false call_unhold(call.callp)
call.hold = false
holdButton.text = "Hold" holdButton.text = "Hold"
} }
"History" -> { "History" -> {
val i = Intent(this@MainActivity, HistoryActivity::class.java) val i = Intent(this@MainActivity, HistoryActivity::class.java)
val b = Bundle() val b = Bundle()
b.putString("aor", accounts[aorSpinner.selectedItemPosition].aoR) b.putString("aor", uas[aorSpinner.selectedItemPosition].aor)
i.putExtras(b) i.putExtras(b)
startActivityForResult(i, HISTORY_CODE) startActivityForResult(i, HISTORY_CODE)
} }
@@ -337,8 +359,8 @@ class MainActivity : AppCompatActivity() {
return true return true
} }
R.id.accounts -> { R.id.accounts -> {
i = Intent(this, EditAccountsActivity::class.java) i = Intent(this, AccountsActivity::class.java)
startActivityForResult(i, EDIT_ACCOUNTS_CODE) startActivityForResult(i, ACCOUNTS_CODE)
return true return true
} }
R.id.contacts -> { R.id.contacts -> {
@@ -372,7 +394,6 @@ class MainActivity : AppCompatActivity() {
Log.w("Baresip", "OutputStream exception: " + e.toString()) Log.w("Baresip", "OutputStream exception: " + e.toString())
e.printStackTrace() e.printStackTrace()
} }
baresipStop() baresipStop()
nm.cancel(10) nm.cancel(10)
running = false running = false
@@ -389,15 +410,12 @@ class MainActivity : AppCompatActivity() {
when (requestCode) { when (requestCode) {
EDIT_ACCOUNTS_CODE -> { ACCOUNTS_CODE -> {
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK)
Utils.alertView(this, "Notice", Log.d("Baresip", "Edit accounts OK")
"You need to restart baresip in order to activate saved accounts!") if (resultCode == RESULT_CANCELED)
}
if (resultCode == RESULT_CANCELED) {
Log.d("Baresip", "Edit accounts canceled") Log.d("Baresip", "Edit accounts canceled")
} }
}
EDIT_CONFIG_CODE -> { EDIT_CONFIG_CODE -> {
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
@@ -418,7 +436,7 @@ class MainActivity : AppCompatActivity() {
} }
if (resultCode == RESULT_CANCELED) { if (resultCode == RESULT_CANCELED) {
Log.d("Baresip", "History canceled") Log.d("Baresip", "History canceled")
if (HistoryActivity.aorHistory(accounts[aorSpinner.selectedItemPosition].aoR) == 0) { if (HistoryActivity.aorHistory(uas[aorSpinner.selectedItemPosition].aor) == 0) {
(findViewById(R.id.holdButton) as Button).visibility = View.INVISIBLE (findViewById(R.id.holdButton) as Button).visibility = View.INVISIBLE
} }
} }
@@ -430,12 +448,12 @@ class MainActivity : AppCompatActivity() {
} }
} }
private fun call(ua: String, uri: String) { private fun call(ua: UserAgent, uri: String) {
(findViewById(R.id.callee) as EditText).setText(uri) (findViewById(R.id.callee) as EditText).setText(uri)
Log.i("Baresip", "Calling $ua / $uri") Log.i("Baresip", "Calling $ua.uap / $uri")
val call = ua_connect(ua, uri) val call = ua_connect(ua.uap, uri)
if (call != "") { if (call != "") {
Log.i("Baresip", "Adding outgoing call $ua / $call / $uri") Log.i("Baresip", "Adding outgoing call $ua.uap / $call / $uri")
val dtmfWatcher = object: TextWatcher { val dtmfWatcher = object: TextWatcher {
override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(sequence: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(sequence: CharSequence, start: Int, before: Int, count: Int) {
@@ -447,15 +465,16 @@ class MainActivity : AppCompatActivity() {
call_send_digit(call, 4.toChar()) call_send_digit(call, 4.toChar())
} }
} }
callsOut.add(Call(ua, call, uri, "Cancel", dtmfWatcher)) calls.add(Call(call, ua, uri, "out","Cancel", dtmfWatcher))
(findViewById(R.id.callButton) as Button).text = "Cancel" (findViewById(R.id.callButton) as Button).text = "Cancel"
(findViewById(R.id.holdButton) as Button).visibility = View.INVISIBLE (findViewById(R.id.holdButton) as Button).visibility = View.INVISIBLE
} }
} }
private fun addCallViews(ua: String, call: Call, id: Int) { private fun addCallViews(ua: UserAgent, call: Call, id: Int) {
Log.d("Baresip", "Creating new Incoming textview at $id") Log.d("Baresip", "Creating new Incoming textview at $id")
val acc = ua.account
val caller_heading = TextView(appContext) val caller_heading = TextView(appContext)
caller_heading.text = "Incoming call from ..." caller_heading.text = "Incoming call from ..."
caller_heading.setTextColor(Color.BLACK) caller_heading.setTextColor(Color.BLACK)
@@ -480,7 +499,7 @@ class MainActivity : AppCompatActivity() {
caller_row.layoutParams = caller_row_params caller_row.layoutParams = caller_row_params
val caller_uri = TextView(appContext) val caller_uri = TextView(appContext)
caller_uri.text = callsIn[callsIn.size - 1].peerURI caller_uri.text = call.peerURI
caller_uri.setTextColor(Color.BLUE) caller_uri.setTextColor(Color.BLUE)
caller_uri.textSize = 20f caller_uri.textSize = 20f
caller_uri.setPadding(10, 10, 0, 10) caller_uri.setPadding(10, 10, 0, 10)
@@ -497,14 +516,42 @@ class MainActivity : AppCompatActivity() {
val security_button_params = LinearLayout.LayoutParams(dp24px, dp24px, 0.0f) val security_button_params = LinearLayout.LayoutParams(dp24px, dp24px, 0.0f)
security_button_params.gravity = Gravity.CENTER_VERTICAL security_button_params.gravity = Gravity.CENTER_VERTICAL
security_button.layoutParams = security_button_params security_button.layoutParams = security_button_params
if ((call.security != 0) && (ua_mediaenc(ua) != "")) { if ((call.security != 0) && (acc.mediaenc != "")) {
security_button.setImageResource(call.security) security_button.setImageResource(call.security)
Utils.setSecurityButtonTag(securityButton, call.security) setSecurityButtonTag(securityButton, call.security)
security_button.visibility = View.VISIBLE security_button.visibility = View.VISIBLE
} else { } else {
security_button.visibility = View.INVISIBLE security_button.visibility = View.INVISIBLE
} }
Utils.setSecurityButtonOnClickListener(this, security_button, call) security_button.setOnClickListener {
when (security_button.tag) {
"red" -> {
Utils.alertView(this, "Alert", "This call is NOT secure!")
}
"yellow" -> {
Utils.alertView(this, "Alert",
"This call is SECURE, but peer is NOT verified!")
}
"green" -> {
val unverifyDialog = AlertDialog.Builder(this)
unverifyDialog.setMessage("This call is SECURE and peer is VERIFIED! " +
"Do you want to unverify the peer?")
unverifyDialog.setPositiveButton("Unverify") { dialog, _ ->
if (Utils.cmd_exec("zrtp_unverify " + call.zid) != 0) {
Log.w("Baresip", "Command 'zrtp_unverify ${call.zid}' failed")
} else {
security_button.setImageResource(R.drawable.box_yellow)
security_button.tag = "yellow"
}
dialog.dismiss()
}
unverifyDialog.setNegativeButton("No") { dialog, _ ->
dialog.dismiss()
}
unverifyDialog.create().show()
}
}
}
caller_row.addView(security_button) caller_row.addView(security_button)
layout.addView(caller_row) layout.addView(caller_row)
@@ -516,20 +563,16 @@ class MainActivity : AppCompatActivity() {
Log.d("Baresip", "Creating new answer button at " + (id + 4)) Log.d("Baresip", "Creating new answer button at " + (id + 4))
answer_button.id = id + 4 answer_button.id = id + 4
answer_button.setOnClickListener { v -> answer_button.setOnClickListener { v ->
val call_ua = call.ua
val call_call = call.call
when ((v as Button).text.toString()) { when ((v as Button).text.toString()) {
"Answer" -> { "Answer" -> {
Log.i("Baresip", "UA " + call_ua + " accepting incoming call " + Log.i("Baresip", "UA ${call.ua.uap} accepting incoming call ${call.callp}")
call_call) ua_answer(call.ua.uap, call.callp)
ua_answer(call_ua, call_call) if (call.dir == "in") {
val final_in_index = Call.callIndex(callsIn, call_ua, call_call)
if (final_in_index >= 0) {
Log.d("Baresip", "Updating Hangup and Hold") Log.d("Baresip", "Updating Hangup and Hold")
callsIn[final_in_index].status = "Hangup" call.status = "Hangup"
callsIn[final_in_index].hold = false call.hold = false
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
val answer_id = (final_in_index + 1) * 10 + 4 val answer_id = (Call.index(calls, call,"in") + 1) * 10 + 4
val answer_but = layout.findViewById(answer_id) as Button val answer_but = layout.findViewById(answer_id) as Button
answer_but.text = "Hangup" answer_but.text = "Hangup"
val reject_button = layout.findViewById(answer_id + 1) as Button val reject_button = layout.findViewById(answer_id + 1) as Button
@@ -538,9 +581,8 @@ class MainActivity : AppCompatActivity() {
} }
} }
"Hangup" -> { "Hangup" -> {
Log.i("Baresip", "UA " + call_ua + " hanging up call " + Log.i("Baresip", "UA ${call.ua.uap} hanging up call ${call.callp}")
call_call) ua_hangup(call.ua.uap, call.callp, 200, "OK")
ua_hangup(call_ua, call_call, 200, "OK")
} }
else -> Log.e("Baresip", "Invalid answer button text: " + v.text.toString()) else -> Log.e("Baresip", "Invalid answer button text: " + v.text.toString())
} }
@@ -569,16 +611,16 @@ class MainActivity : AppCompatActivity() {
when ((v as Button).text.toString()) { when ((v as Button).text.toString()) {
"Reject" -> { "Reject" -> {
Log.i("Baresip", "UA " + call.ua + Log.i("Baresip", "UA " + call.ua +
" rejecting incoming call " + call.call) " rejecting incoming call " + call.callp)
ua_hangup(call.ua, call.call, 486, "Rejected") ua_hangup(call.ua.uap, call.callp, 486, "Rejected")
} }
"Hold" -> { "Hold" -> {
call_hold(call.call) call_hold(call.callp)
v.text = "Unhold" v.text = "Unhold"
call.hold = true call.hold = true
} }
"Unhold" -> { "Unhold" -> {
call_unhold(call.call) call_unhold(call.callp)
v.text = "Hold" v.text = "Hold"
call.hold = false call.hold = false
} }
@@ -596,14 +638,14 @@ class MainActivity : AppCompatActivity() {
val contentView = RemoteViews(getPackageName(), R.layout.notification) val contentView = RemoteViews(getPackageName(), R.layout.notification)
for (i: Int in 0 .. 3) { for (i: Int in 0 .. 3) {
val resID = resources.getIdentifier("status$i", "id", packageName); val resID = resources.getIdentifier("status$i", "id", packageName);
if (i < accounts.size) { if (i < images.size) {
contentView.setImageViewResource(resID, images[i]) contentView.setImageViewResource(resID, images[i])
contentView.setViewVisibility(resID, View.VISIBLE) contentView.setViewVisibility(resID, View.VISIBLE)
} else { } else {
contentView.setViewVisibility(resID, View.INVISIBLE) contentView.setViewVisibility(resID, View.INVISIBLE)
} }
} }
if (accounts.size > 4) if (images.size > 4)
contentView.setViewVisibility(R.id.etc, View.VISIBLE) contentView.setViewVisibility(R.id.etc, View.VISIBLE)
else else
contentView.setViewVisibility(R.id.etc, View.INVISIBLE) contentView.setViewVisibility(R.id.etc, View.INVISIBLE)
@@ -613,63 +655,70 @@ class MainActivity : AppCompatActivity() {
@Keep @Keep
fun addAccount(ua: String) { fun addAccount(ua: String) {
val aor = ua_aor(ua) val userAgent = UserAgent(ua)
Log.d("Baresip", "Adding account $ua with AoR $aor") uas.add(userAgent)
accounts.add(Account(ua, aor, ua_mediaenc(ua),""))
images.add(R.drawable.dot_yellow) images.add(R.drawable.dot_yellow)
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
accountAdapter.notifyDataSetChanged() uaAdapter.notifyDataSetChanged()
updateNotification() updateNotification()
} }
} }
@Keep @Keep
private fun updateStatus(event: String, ua: String, call: String) { private fun updateStatus(event: String, uap: String, callp: String) {
val aor = ua_aor(ua) val ua = UserAgent.find(uas, uap)
if (ua == null) {
Log.e("Baresip", "Update status did not find ua $uap")
return
}
val aor = ua.aor
val acc = ua.account
val ev = event.split(",") val ev = event.split(",")
Log.d("Baresip", "Handling event ${ev[0]} for $ua/$call/$aor") Log.d("Baresip", "Handling event ${ev[0]} for $uap/$callp/$aor")
for (account_index in accounts.indices) { for (account_index in uas.indices) {
if (accounts[account_index].aoR == aor) { if (uas[account_index].aor == aor) {
Log.d("Baresip", "Found AoR at index $account_index") Log.d("Baresip", "Found AoR at index $account_index")
when (ev[0]) { when (ev[0]) {
"registering", "unregistering" -> { "registering", "unregistering" -> {
} }
"registered" -> { "registered" -> {
Log.d("Baresip", "Setting status to green") Log.d("Baresip", "Setting status to green")
accounts[account_index].status = "OK"
images[account_index] = R.drawable.dot_green images[account_index] = R.drawable.dot_green
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
accountAdapter.notifyDataSetChanged() uaAdapter.notifyDataSetChanged()
updateNotification() updateNotification()
} }
} }
"registering failed" -> { "registering failed" -> {
Log.d("Baresip", "Setting status to red") Log.d("Baresip", "Setting status to red")
accounts[account_index].status = "FAIL"
images[account_index] = R.drawable.dot_red images[account_index] = R.drawable.dot_red
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
accountAdapter.notifyDataSetChanged() uaAdapter.notifyDataSetChanged()
updateNotification() updateNotification()
} }
} }
"call ringing" -> { "call ringing" -> {
} }
"call established" -> { "call established" -> {
val out_index = Call.callIndex(callsOut, ua, call) val call = Call.find(calls, callp)
if (out_index != -1) { if (call == null) {
Log.d("Baresip", "Outbound call $call established") Log.e("Baresip", "Established call $callp not found")
callsOut[out_index].status = "Hangup" return
callsOut[out_index].hold = false }
callsOut[out_index].security = R.drawable.box_red if (call.dir == "out") {
Log.d("Baresip", "Outbound call $callp established")
call.status = "Hangup"
call.hold = false
call.security = R.drawable.box_red
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
if (ua == accounts[aorSpinner.selectedItemPosition].ua) { if (ua == uas[aorSpinner.selectedItemPosition]) {
callButton.text = "Hangup" callButton.text = "Hangup"
holdButton.text = "Hold" holdButton.text = "Hold"
holdButton.visibility = View.VISIBLE holdButton.visibility = View.VISIBLE
if (ua_mediaenc(ua) == "") { if (acc.mediaenc == "") {
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
} else { } else {
securityButton.setImageResource(R.drawable.box_red) securityButton.setImageResource(R.drawable.box_red)
@@ -680,36 +729,34 @@ class MainActivity : AppCompatActivity() {
dtmf.hint = "DTMF" dtmf.hint = "DTMF"
dtmf.visibility = View.VISIBLE dtmf.visibility = View.VISIBLE
dtmf.requestFocus() dtmf.requestFocus()
dtmf.addTextChangedListener(callsOut[out_index].dtmfWatcher) dtmf.addTextChangedListener(call.dtmfWatcher)
} }
} }
HistoryActivity.History.add(History(ua, call, aor, call_peeruri(call), HistoryActivity.History.add(History(aor, call.peerURI, "out",
"out", true)) true))
call.hasHistory = true
} else { } else {
val in_index = Call.callIndex(callsIn, ua, call) Log.d("Baresip", "Inbound call $callp established")
if (in_index != -1) { call.status = "Answer"
Log.d("Baresip", "Inbound call $call established") call.security = R.drawable.box_red
callsIn[in_index].status = "Answer"
callsIn[in_index].security = R.drawable.box_red
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
if (ua == accounts[aorSpinner.selectedItemPosition].ua) { if (ua == uas[aorSpinner.selectedItemPosition]) {
if (ua_mediaenc(ua) != "") { if (acc.mediaenc != "") {
var view_id = (in_index + 1) * 10 + 3 val inIndex = Call.index(calls, call, "in")
var view_id = (inIndex + 1) * 10 + 3
val securityButton = findViewById(view_id) as ImageButton val securityButton = findViewById(view_id) as ImageButton
securityButton.setImageResource(R.drawable.box_red) securityButton.setImageResource(R.drawable.box_red)
securityButton.tag = "red" securityButton.tag = "red"
securityButton.visibility = View.VISIBLE securityButton.visibility = View.VISIBLE
view_id = (in_index + 1) * 10 + 5 view_id = (inIndex + 1) * 10 + 5
val rejectButton = findViewById(view_id) as Button val rejectButton = findViewById(view_id) as Button
rejectButton.text = "Hold" rejectButton.text = "Hold"
} }
} }
} }
HistoryActivity.History.add(History(ua, call, aor, call_peeruri(call), HistoryActivity.History.add(History(aor, call.peerURI, "in",
"in", true)) true))
} else { call.hasHistory = true
Log.e("Baresip", "Unknown call $ua/$call established")
}
} }
am.mode = AudioManager.MODE_IN_COMMUNICATION am.mode = AudioManager.MODE_IN_COMMUNICATION
am.isSpeakerphoneOn = false am.isSpeakerphoneOn = false
@@ -720,78 +767,68 @@ class MainActivity : AppCompatActivity() {
HistoryActivity.aorRemoveHistory(aor) HistoryActivity.aorRemoveHistory(aor)
} }
"call incoming" -> { "call incoming" -> {
val peer_uri = call_peeruri(call) val peer_uri = call_peeruri(callp)
Log.d("Baresip", "Incoming call " + ua + "/" + call + "/" + Log.d("Baresip", "Incoming call $uap/$callp/$peer_uri")
peer_uri) val new_call = Call(callp, ua, peer_uri, "in", "Answer", null)
val new_call = Call(ua, call, peer_uri, "Answer", null) calls.add(new_call)
callsIn.add(new_call)
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
if (ua == accounts[aorSpinner.selectedItemPosition].ua) { if (ua == uas[aorSpinner.selectedItemPosition]) {
addCallViews(ua, new_call, callsIn.size * 10) addCallViews(ua, new_call, Call.calls(calls, "in").size * 10)
} }
} }
am.mode = AudioManager.MODE_RINGTONE am.mode = AudioManager.MODE_RINGTONE
// am.isSpeakerphoneOn = true // am.isSpeakerphoneOn = true
} }
"call verify" -> { "call verify" -> {
val call = Call.find(calls, callp)
if (call == null) {
Log.e("Baresip", "Call $callp to be verified is not found")
return
}
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
val verifyDialog = AlertDialog.Builder(this) val verifyDialog = AlertDialog.Builder(this)
verifyDialog.setTitle("Verify") verifyDialog.setTitle("Verify")
verifyDialog.setMessage("Do you want to verify SAS <${ev[1]}> <${ev[2]}>?") verifyDialog.setMessage("Do you want to verify SAS <${ev[1]}> <${ev[2]}>?")
verifyDialog.setPositiveButton("Yes") { dialog, _ -> verifyDialog.setPositiveButton("Yes") { dialog, _ ->
val security: Int val security: Int
if (cmd_exec("zrtp_verify ${ev[3]}") != 0) { if (Utils.cmd_exec("zrtp_verify ${ev[3]}") != 0) {
Log.w("Baresip", "Command 'zrtp_verify ${ev[3]}' failed") Log.w("Baresip", "Command 'zrtp_verify ${ev[3]}' failed")
security = R.drawable.box_yellow security = R.drawable.box_yellow
} else { } else {
security = R.drawable.box_green security = R.drawable.box_green
} }
var index = Call.callIndex(callsOut, ua, call) call.security = security
if (index != -1) { call.zid = ev[3]
callsOut[index].security = security if (call.dir == "out") {
callsOut[index].zid = ev[3]
securityButton.setImageResource(security) securityButton.setImageResource(security)
Utils.setSecurityButtonTag(securityButton, security) setSecurityButtonTag(securityButton, security)
securityButton.visibility = View.VISIBLE securityButton.visibility = View.VISIBLE
} else { } else {
index = Call.callIndex(callsIn, ua, call) if (ua == uas[aorSpinner.selectedItemPosition]) {
if (index != -1) { val view_id = (Call.index(calls, call, "in") + 1) * 10 + 3
callsIn[index].security = security
callsIn[index].zid = ev[3]
if (ua == accounts[aorSpinner.selectedItemPosition].ua) {
val view_id = (index + 1) * 10 + 3
val securityButton = layout.findViewById(view_id) as ImageButton val securityButton = layout.findViewById(view_id) as ImageButton
securityButton.setImageResource(security) securityButton.setImageResource(security)
Utils.setSecurityButtonTag(securityButton, security) setSecurityButtonTag(securityButton, security)
securityButton.visibility = View.VISIBLE securityButton.visibility = View.VISIBLE
} }
} }
}
dialog.dismiss() dialog.dismiss()
} }
verifyDialog.setNegativeButton("No") { dialog, _ -> verifyDialog.setNegativeButton("No") { dialog, _ ->
var index = Call.callIndex(callsOut, ua, call) call.security = R.drawable.box_yellow
if (index >= 0) { call.zid = ev[3]
callsOut[index].security = R.drawable.box_yellow if (call.dir == "out") {
callsOut[index].zid = ev[3]
securityButton.setImageResource(R.drawable.box_yellow) securityButton.setImageResource(R.drawable.box_yellow)
securityButton.tag = "yellow" securityButton.tag = "yellow"
securityButton.visibility = View.VISIBLE securityButton.visibility = View.VISIBLE
} else { } else {
index = Call.callIndex(callsIn, ua, call) if (ua == uas[aorSpinner.selectedItemPosition]) {
if (index != -1) { val view_id = (Call.index(calls, call, "in") + 1) * 10 + 3
callsIn[index].security = R.drawable.box_yellow
callsIn[index].zid = ev[3]
if (ua == accounts[aorSpinner.selectedItemPosition].ua) {
val view_id = (index + 1) * 10 + 3
val securityButton = layout.findViewById(view_id) as ImageButton val securityButton = layout.findViewById(view_id) as ImageButton
securityButton.setImageResource(R.drawable.box_yellow) securityButton.setImageResource(R.drawable.box_yellow)
securityButton.tag = "yellow" securityButton.tag = "yellow"
securityButton.visibility = View.VISIBLE securityButton.visibility = View.VISIBLE
} }
} else {
Log.e("Baresip", "Unknown call $ua/$call to verify")
}
} }
dialog.dismiss() dialog.dismiss()
} }
@@ -799,46 +836,48 @@ class MainActivity : AppCompatActivity() {
} }
} }
"call verified" -> { "call verified" -> {
val out_index = Call.callIndex(callsOut, ua, call) val call = Call.find(calls, callp)
if (out_index != -1) { if (call == null) {
callsOut[out_index].security = R.drawable.box_green Log.e("Baresip", "Call $callp that is verified is not found")
callsOut[out_index].zid = ev[1] return
}
call.security = R.drawable.box_green
call.zid = ev[1]
if (call.dir == "out") {
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
if (ua == accounts[aorSpinner.selectedItemPosition].ua) { if (ua == uas[aorSpinner.selectedItemPosition]) {
securityButton.setImageResource(R.drawable.box_green) securityButton.setImageResource(R.drawable.box_green)
securityButton.tag = "green" securityButton.tag = "green"
securityButton.visibility = View.VISIBLE securityButton.visibility = View.VISIBLE
} }
} }
} else { } else {
val in_index = Call.callIndex(callsIn, ua, call)
if (in_index != -1) {
callsIn[in_index].security = R.drawable.box_green
callsIn[in_index].zid = ev[1]
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
if (ua == accounts[aorSpinner.selectedItemPosition].ua) { if (ua == uas[aorSpinner.selectedItemPosition]) {
val view_id = (in_index + 1) * 10 + 3 val view_id = (Call.index(calls, call, "in") + 1) * 10 + 3
val securityButton = layout.findViewById(view_id) as ImageButton val securityButton = layout.findViewById(view_id) as ImageButton
securityButton.setImageResource(R.drawable.box_green) securityButton.setImageResource(R.drawable.box_green)
securityButton.tag = "green" securityButton.tag = "green"
securityButton.visibility = View.VISIBLE securityButton.visibility = View.VISIBLE
} }
} }
} else {
Log.e("Baresip", "Unknown call $ua/$call verified")
}
} }
} }
"call closed" -> { "call closed" -> {
val in_index = Call.callIndex(callsIn, ua, call) val call = Call.find(calls, callp)
if (in_index != -1) { if (call == null) {
Log.d("Baresip", "Removing inbound call " + ua + "/" + Log.e("Baresip", "Call $callp that is closed is not found")
call + "/" + callsIn[in_index].peerURI) return
val view_id = (in_index + 1) * 10 }
val remove_count = callsIn.size - in_index if (call.dir == "in") {
callsIn.removeAt(in_index) Log.d("Baresip", "Removing inbound call $uap / $callp / " +
call.peerURI)
val inIndex = Call.index(calls, call, "in")
val view_id = (inIndex + 1) * 10
val remove_count = Call.calls(calls, "in").size - inIndex
calls.removeAt(Call.index(calls, call, ""))
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
if (ua == accounts[aorSpinner.selectedItemPosition].ua) { if (ua == uas[aorSpinner.selectedItemPosition]) {
if (callButton.text == "Call") { if (callButton.text == "Call") {
holdButton.text = "History" holdButton.text = "History"
holdButton.visibility = View.VISIBLE holdButton.visibility = View.VISIBLE
@@ -847,24 +886,23 @@ class MainActivity : AppCompatActivity() {
val caller_heading = layout.findViewById(view_id) val caller_heading = layout.findViewById(view_id)
val view_index = layout.indexOfChild(caller_heading) val view_index = layout.indexOfChild(caller_heading)
layout.removeViews(view_index, 4 * remove_count) layout.removeViews(view_index, 4 * remove_count)
for (i in in_index until callsIn.size) { val callsIn = Call.calls(calls, "in")
for (i in inIndex until callsIn.size) {
this@MainActivity.addCallViews(ua, callsIn[i], (i + 1) * 10) this@MainActivity.addCallViews(ua, callsIn[i], (i + 1) * 10)
} }
} }
} }
if (!HistoryActivity.callHasHistory(ua, call)) { if (!call.hasHistory) {
if (HistoryActivity.aorHistory(aor) > HISTORY_SIZE) if (HistoryActivity.aorHistory(aor) > HISTORY_SIZE)
HistoryActivity.aorRemoveHistory(aor) HistoryActivity.aorRemoveHistory(aor)
HistoryActivity.History.add(History(ua, call, aor, call_peeruri(call), HistoryActivity.History.add(History(aor, call.peerURI,
"in", false)) "in", false))
} }
} else { } else {
val out_index = Call.callIndex(callsOut, ua, call) Log.d("Baresip", "Removing outgoing call $uap / $callp / " +
if (out_index != -1) { call.peerURI)
Log.d("Baresip", "Removing outgoing call " + ua + "/" +
call + "/" + callsOut[out_index].peerURI)
this@MainActivity.runOnUiThread { this@MainActivity.runOnUiThread {
if (ua == accounts[aorSpinner.selectedItemPosition].ua) { if (ua == uas[aorSpinner.selectedItemPosition]) {
callButton.text = "Call" callButton.text = "Call"
callButton.isEnabled = true callButton.isEnabled = true
callee.setText("") callee.setText("")
@@ -875,24 +913,19 @@ class MainActivity : AppCompatActivity() {
imm.hideSoftInputFromWindow(dtmf.getWindowToken(), 0) imm.hideSoftInputFromWindow(dtmf.getWindowToken(), 0)
} }
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
dtmf.removeTextChangedListener(callsOut[out_index].dtmfWatcher) dtmf.removeTextChangedListener(call.dtmfWatcher)
dtmf.visibility = View.INVISIBLE dtmf.visibility = View.INVISIBLE
} }
callsOut.removeAt(out_index) calls.removeAt(Call.index(calls, call, ""))
} }
if (!HistoryActivity.callHasHistory(ua, call)) { if (!call.hasHistory) {
if (HistoryActivity.aorHistory(aor) > HISTORY_SIZE) if (HistoryActivity.aorHistory(aor) > HISTORY_SIZE)
HistoryActivity.aorRemoveHistory(aor) HistoryActivity.aorRemoveHistory(aor)
HistoryActivity.History.add(History(ua, call, aor, call_peeruri(call), HistoryActivity.History.add(History(aor, call.peerURI,
"out", false)) "out", false))
} }
} else {
Log.e("Baresip", "Unknown call " + ua + "/" + call +
" closed")
} }
} if (calls.size == 0) am.mode = AudioManager.MODE_NORMAL
if ((callsIn.size == 0) && (callsOut.size == 0))
am.mode = AudioManager.MODE_NORMAL
} }
else -> Log.d("Baresip", "Unknown event '${ev[0]}'") else -> Log.d("Baresip", "Unknown event '${ev[0]}'")
} }
@@ -900,33 +933,56 @@ class MainActivity : AppCompatActivity() {
} }
} }
private fun copyAssetToFile(context: Context, asset: String, path: String) {
try {
val `is` = context.assets.open(asset)
val os = FileOutputStream(path)
val buffer = ByteArray(512)
var byteRead: Int = `is`.read(buffer)
while (byteRead != -1) {
os.write(buffer, 0, byteRead)
byteRead = `is`.read(buffer)
}
} catch (e: IOException) {
Log.e("Baresip", "Failed to read asset " + asset + ": " +
e.toString())
}
}
private fun setSecurityButtonTag(button: ImageButton, security: Int) {
when (security) {
R.drawable.box_red -> { button.tag = "red" }
R.drawable.box_yellow -> { button.tag = "yellow" }
R.drawable.box_green -> { button.tag = "green" }
}
}
external fun baresipStart(path: String) external fun baresipStart(path: String)
external fun baresipStop() external fun baresipStop()
external fun call_peeruri(call: String): String
external fun ua_aor(ua: String): String
external fun ua_mediaenc(ua: String): String
external fun ua_connect(ua: String, peer_uri: String): String external fun ua_connect(ua: String, peer_uri: String): String
external fun ua_answer(ua: String, call: String) external fun ua_answer(ua: String, call: String)
external fun ua_hangup(ua: String, call: String, code: Int, reason: String) external fun ua_hangup(ua: String, call: String, code: Int, reason: String)
external fun call_peeruri(call: String): String
external fun call_hold(call: String): Int external fun call_hold(call: String): Int
external fun call_unhold(call: String): Int external fun call_unhold(call: String): Int
external fun call_send_digit(call: String, digit: Char): Int external fun call_send_digit(call: String, digit: Char): Int
external fun cmd_exec(cmd: String): Int
external fun reload_config(): Int external fun reload_config(): Int
companion object { companion object {
internal var running: Boolean = false internal var running: Boolean = false
internal var accounts = ArrayList<Account>() var uas = ArrayList<UserAgent>()
internal var images = ArrayList<Int>() internal var images = ArrayList<Int>()
internal var callsIn = ArrayList<Call>() internal var calls = ArrayList<Call>()
internal var callsOut = ArrayList<Call>() var filesPath = ""
const val EDIT_ACCOUNTS_CODE = 1 const val ACCOUNTS_CODE = 1
const val EDIT_CONTACTS_CODE = 2 const val EDIT_CONTACTS_CODE = 2
const val EDIT_CONFIG_CODE = 3 const val EDIT_CONFIG_CODE = 3
const val HISTORY_CODE = 4 const val HISTORY_CODE = 4
const val ABOUT_CODE = 5 const val ABOUT_CODE = 5
const val ACCOUNT_CODE = 6
const val RECORD_AUDIO_PERMISSION = 1 const val RECORD_AUDIO_PERMISSION = 1
const val HISTORY_SIZE = 100 const val HISTORY_SIZE = 100
@@ -0,0 +1,37 @@
package com.tutpro.baresip
import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.TextView
import java.util.ArrayList
class UaSpinnerAdapter(private val cxt: Context, private val uas: ArrayList<UserAgent>,
private val images: ArrayList<Int>) :
ArrayAdapter<Int>(cxt, android.R.layout.simple_spinner_item, images) {
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
return getImageForPosition(position, parent)
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
return getImageForPosition(position, parent)
}
private fun getImageForPosition(position: Int, parent: ViewGroup): View {
val inflater = cxt.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val row = inflater.inflate(R.layout.account_spinner, parent, false)
val textView = row.findViewById(R.id.spinnerText) as TextView
textView.text = uas[position].aor.replace("sip:", "")
textView.textSize = 17f
val imageView = row.findViewById(R.id.spinnerImage) as ImageView
imageView.setImageResource(images[position])
return row
}
}
@@ -0,0 +1,35 @@
package com.tutpro.baresip
class UserAgent (val uap: String) {
val account = Account(ua_account(uap))
val aor = ua_aor(uap)
companion object {
external fun ua_alloc(uri: String): Int
external fun ua_destroy(uri: String)
external fun ua_register(ua: String): Int
external fun ua_isregistered(ua: String): Boolean
external fun ua_unregister(ua: String)
fun userAgents(): ArrayList<UserAgent> {
val res = ArrayList<UserAgent>()
for (ua in MainActivity.uas) {
res.add(ua)
}
return res
}
fun find(uas: ArrayList<UserAgent>, uap: String): UserAgent? {
for (u in uas) {
if (u.uap == uap) return u
}
return null
}
}
}
external fun ua_account(ua: String): String
external fun ua_aor(ua: String): String
+35 -47
View File
@@ -3,28 +3,11 @@ package com.tutpro.baresip
import android.content.Context import android.content.Context
import android.support.v7.app.AlertDialog import android.support.v7.app.AlertDialog
import android.util.Log import android.util.Log
import android.widget.ImageButton
import java.io.* import java.io.*
object Utils { object Utils {
fun copyAssetToFile(context: Context, asset: String, path: String) {
try {
val `is` = context.assets.open(asset)
val os = FileOutputStream(path)
val buffer = ByteArray(512)
var byteRead: Int = `is`.read(buffer)
while (byteRead != -1) {
os.write(buffer, 0, byteRead)
byteRead = `is`.read(buffer)
}
} catch (e: IOException) {
Log.e("Baresip", "Failed to read asset " + asset + ": " +
e.toString())
}
}
fun getFileContents(file: File): String { fun getFileContents(file: File): String {
if (!file.exists()) { if (!file.exists()) {
Log.e("Baresip", "Failed to find file: " + file.path) Log.e("Baresip", "Failed to find file: " + file.path)
@@ -89,46 +72,51 @@ object Utils {
return uri.substringAfter(":").substringBefore("@") return uri.substringAfter(":").substringBefore("@")
} }
fun setSecurityButtonTag(button: ImageButton, security: Int) { fun checkUserID(id: String): Boolean {
when (security) { return Regex("^[a-zA-Z]([._-]|[a-zA-Z0-9]){1,49}\$").matches(id)
R.drawable.box_red -> { button.tag = "red" }
R.drawable.box_yellow -> { button.tag = "yellow" }
R.drawable.box_green -> { button.tag = "green" }
}
} }
fun setSecurityButtonOnClickListener(context: Context, button: ImageButton, call: Call) { fun checkTelNo(no: String): Boolean {
button.setOnClickListener { return Regex("^[+]?[0-9]{1,16}\$").matches(no)
when (button.tag) {
"red" -> {
Utils.alertView(context, "Alert", "This call is NOT secure!")
} }
"yellow" -> {
Utils.alertView(context, "Alert", fun checkIP(ip: String): Boolean {
"This call is SECURE, but peer is NOT verified!") return Regex("^(([0-1]?[0-9]{1,2}\\.)|(2[0-4][0-9]\\.)|(25[0-5]\\.)){3}(([0-1]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))/$").matches(ip)
} }
"green" -> {
val unverifyDialog = AlertDialog.Builder(context) fun checkDomain(domain: String): Boolean {
unverifyDialog.setMessage("This call is SECURE and peer is VERIFIED! " + val parts = domain.split(".")
"Do you want to unverify the peer?") for (p in parts) {
unverifyDialog.setPositiveButton("Unverify") { dialog, _ -> if (p.length == 0 || p.endsWith("-") || !Regex("^[a-zA-z]([-]|[a-zA-Z0-9])+\$").matches(p))
if (cmd_exec("zrtp_unverify " + call.zid) != 0) { return false
Log.w("Baresip", "Command 'zrtp_unverify ${call.zid}' failed")
} else {
button.setImageResource(R.drawable.box_yellow)
button.tag = "yellow"
} }
dialog.dismiss() return true
} }
unverifyDialog.setNegativeButton("No") { dialog, _ ->
dialog.dismiss() fun checkPrintASCII(s: String): Boolean {
} if (s == "") return true
unverifyDialog.create().show() val printASCIIRegex = Regex("^[ -~]*\$")
return printASCIIRegex.matches(s)
} }
fun checkUint(s: String): Boolean {
val uintRegex = Regex("^[0-9]+\$")
return uintRegex.matches(s)
} }
fun implode(list: List<String>, sep: String): String {
var res = ""
for (s in list) {
if (res == "")
res = s
else
res = res + sep + s
} }
return res
} }
external fun cmd_exec(cmd: String): Int external fun cmd_exec(cmd: String): Int
external fun uri_decode(uri: String): Boolean
external fun audio_codecs(): String
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#ffffff" />
</shape>
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/accountRow"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/aor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:textSize="20sp"
android:layout_marginBottom="6dp">
</TextView>
<ImageButton
android:id="@+id/action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_marginLeft="6dp"
android:icon="@android:drawable/ic_menu_delete"
android:scaleType="centerCrop" >
</ImageButton>
</RelativeLayout>
@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/AccountView"
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" >
<TextView
android:id="@+id/DisplayNameTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:focusable="true"
android:focusableInTouchMode="true"
android:text="Display Name" >
</TextView>
<EditText
android:id="@+id/DisplayName"
android:layout_height="wrap_content"
android:hint="Your Name"
android:inputType="textPersonName"
android:textSize="18sp"
android:layout_width="fill_parent">
</EditText>
<TextView
android:id="@+id/AuthUserTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:text="Authentication Username" >
</TextView>
<EditText
android:id="@+id/AuthUser"
android:layout_height="wrap_content"
android:hint="User ID"
android:textSize="18sp"
android:layout_width="fill_parent">
</EditText>
<TextView
android:id="@+id/AuthPassTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:text="Authentication Password" >
</TextView>
<EditText
android:id="@+id/AuthPass"
android:layout_height="wrap_content"
android:hint="Password"
android:textSize="18sp"
android:inputType="textPassword"
android:layout_width="fill_parent">
</EditText>
<TextView
android:id="@+id/OutboundProxyTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:text="Outbound Proxies" >
</TextView>
<EditText
android:id="@+id/Outbound1"
android:layout_height="wrap_content"
android:hint="SIP URI of Proxy Server"
android:textSize="18sp"
android:inputType="textUri"
android:layout_width="fill_parent">
</EditText>
<EditText
android:id="@+id/Outbound2"
android:layout_height="wrap_content"
android:hint="SIP URI of another Proxy Server"
android:textSize="18sp"
android:inputType="textUri"
android:layout_width="fill_parent">
</EditText>
<TextView
android:id="@+id/RegIntTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:text="Registration Interval (sec)" >
</TextView>
<EditText
android:id="@+id/RegInt"
android:layout_height="wrap_content"
android:textSize="18sp"
android:inputType="number"
android:layout_width="fill_parent">
</EditText>
<TextView
android:id="@+id/AudioCodecsTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:text="Audio Codecs" >
</TextView>
<LinearLayout
android:id="@+id/CodecSpinners"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
</LinearLayout>
<TextView
android:id="@+id/MediaEncTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:textSize="18sp"
android:textColor="@android:color/black"
android:text="Media Encryption" >
</TextView>
<Spinner
android:id="@+id/mediaEncSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:popupBackground="@drawable/spinner_background" >
</Spinner>
</LinearLayout>
</ScrollView>
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
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.AccountsActivity">
<ListView
android:id="@+id/accounts"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<EditText
android:id="@+id/newAor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:textSize="20sp"
android:layout_alignParentStart="true"
android:layout_marginStart="0dp"
android:hint="SIP URI user@domain" >
</EditText>
<ImageButton
android:id="@+id/addAccount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:layout_marginLeft="6dp"
android:src="@drawable/action_add"
android:scaleType="centerCrop" >
</ImageButton>
</RelativeLayout>
</LinearLayout>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/historyRow"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_height="fill_parent"
android:layout_marginBottom="0dp" android:layout_marginBottom="0dp"