- all user interface unrelated data is now kept in baresip service

allowing proper recovery in case Android kills other activities
- many other improvements and bug fixes
This commit is contained in:
Juha Heinanen
2018-11-23 10:35:48 +02:00
parent 0a9108f846
commit 18478c3436
23 changed files with 872 additions and 679 deletions
+2 -2
View File
@@ -7,8 +7,8 @@ android {
applicationId = 'com.tutpro.baresip' applicationId = 'com.tutpro.baresip'
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 27 targetSdkVersion 27
versionCode = 33 versionCode = 34
versionName = '4.0.0' versionName = '4.1.0'
externalNativeBuild { externalNativeBuild {
cmake { cmake {
cFlags '-DHAVE_INTTYPES_H' cFlags '-DHAVE_INTTYPES_H'
+76 -85
View File
@@ -748,8 +748,7 @@ Java_com_tutpro_baresip_AccountKt_account_1vm_1uri(JNIEnv *env, jobject thiz, js
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1alloc(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_ua_1alloc(JNIEnv *env, jobject thiz, jstring javaUri) {
jstring javaUri) {
const char *uri = (*env)->GetStringUTFChars(env, javaUri, 0); const char *uri = (*env)->GetStringUTFChars(env, javaUri, 0);
struct ua *ua; struct ua *ua;
LOGD("allocating UA '%s'\n", uri); LOGD("allocating UA '%s'\n", uri);
@@ -762,8 +761,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1alloc(JNIEnv *env, jobject
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1register(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_ua_1register(JNIEnv *env, jobject thiz, jstring javaUA)
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);
@@ -772,8 +770,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1register(JNIEnv *env, jobje
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1unregister(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_ua_1unregister(JNIEnv *env, jobject thiz, jstring javaUA)
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);
@@ -782,8 +779,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1unregister(JNIEnv *env, job
} }
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1isregistered(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_ua_1isregistered(JNIEnv *env, jobject thiz, jstring javaUA)
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);
@@ -792,8 +788,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1isregistered(JNIEnv *env, j
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1update_1account(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_ua_1update_1account(JNIEnv *env, jobject thiz, jstring javaUA)
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);
@@ -802,8 +797,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1update_1account(JNIEnv *env
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1destroy(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_ua_1destroy(JNIEnv *env, jobject thiz, jstring javaUA)
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);
@@ -812,7 +806,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1destroy(JNIEnv *env, jobjec
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_UserAgentKt_ua_1account(JNIEnv *env, jobject thiz, jstring javaUA) Java_com_tutpro_baresip_Api_ua_1account(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);
@@ -828,8 +822,7 @@ Java_com_tutpro_baresip_UserAgentKt_ua_1account(JNIEnv *env, jobject thiz, jstri
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_tutpro_baresip_MainActivity_uag_1current_1set(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_uag_1current_1set(JNIEnv *env, jobject thiz, jstring javaUA)
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);
@@ -849,9 +842,47 @@ Java_com_tutpro_baresip_Api_call_1peeruri(JNIEnv *env, jobject thiz, jstring jav
return (*env)->NewStringUTF(env, call_peeruri(call)); return (*env)->NewStringUTF(env, call_peeruri(call));
} }
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_Api_call_1send_1digit(JNIEnv *env, jobject thiz, jstring javaCall,
jchar digit) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
const uint16_t native_digit = digit;
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
LOGD("sending DTMF digit '%c' to call %s\n", (char)native_digit, native_call);
re_thread_enter();
int res = call_send_digit(call, (char)native_digit);
if (!res) res = call_send_digit(call, KEYCODE_REL);
re_thread_leave();
(*env)->ReleaseStringUTFChars(env, javaCall, native_call);
return res;
}
JNIEXPORT void JNICALL
Java_com_tutpro_baresip_Api_ua_1hangup(JNIEnv *env, jobject thiz,
jstring javaUA, jstring javaCall, jint code,
jstring reason) {
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
const uint16_t native_code = code;
const char *native_reason = (*env)->GetStringUTFChars(env, reason, 0);
LOGD("hanging up call %s/%s\n", native_ua, native_call);
re_thread_enter();
if (strlen(native_reason) == 0)
ua_hangup(ua, call, native_code, NULL);
else
ua_hangup(ua, call, native_code, native_reason);
re_thread_leave();
(*env)->ReleaseStringUTFChars(env, javaUA, native_ua);
(*env)->ReleaseStringUTFChars(env, javaCall, native_call);
(*env)->ReleaseStringUTFChars(env, reason, native_reason);
return;
}
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_MainActivity_ua_1connect(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_ua_1connect(JNIEnv *env, jobject thiz, jstring javaUA,
jstring javaUA, jstring javaURI) { jstring javaURI) {
struct call *call; struct call *call;
struct ua *ua; struct ua *ua;
int err; int err;
@@ -875,8 +906,8 @@ Java_com_tutpro_baresip_MainActivity_ua_1connect(JNIEnv *env, jobject thiz,
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_MainActivity_ua_1call_1alloc(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_ua_1call_1alloc(JNIEnv *env, jobject thiz, jstring javaUA,
jstring javaUA, jstring javaXCall) { jstring javaXCall) {
struct call *xcall, *call = NULL; struct call *xcall, *call = NULL;
struct ua *ua; struct ua *ua;
int err; int err;
@@ -901,8 +932,8 @@ Java_com_tutpro_baresip_MainActivity_ua_1call_1alloc(JNIEnv *env, jobject thiz,
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_tutpro_baresip_MainActivity_ua_1answer(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_ua_1answer(JNIEnv *env, jobject thiz, jstring javaUA,
jstring javaUA, jstring javaCall) { jstring javaCall) {
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0); const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0); const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
LOGD("answering call %s/%s\n", native_ua, native_call); LOGD("answering call %s/%s\n", native_ua, native_call);
@@ -918,7 +949,7 @@ Java_com_tutpro_baresip_MainActivity_ua_1answer(JNIEnv *env, jobject thiz,
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_MainActivity_call_1connect(JNIEnv *env, jobject thiz, jstring javaCall, Java_com_tutpro_baresip_Api_call_1connect(JNIEnv *env, jobject thiz, jstring javaCall,
jstring javaPeer) { jstring javaPeer) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0); const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
const char *native_peer = (*env)->GetStringUTFChars(env, javaPeer, 0); const char *native_peer = (*env)->GetStringUTFChars(env, javaPeer, 0);
@@ -936,7 +967,23 @@ Java_com_tutpro_baresip_MainActivity_call_1connect(JNIEnv *env, jobject thiz, js
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_tutpro_baresip_MainActivity_call_1start_1audio(JNIEnv *env, jobject thiz, jstring javaCall) { Java_com_tutpro_baresip_Api_call_1notify_1sipfrag(JNIEnv *env, jobject thiz, jstring javaCall,
jint code, jstring reason) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
const uint16_t native_code = code;
const char *native_reason = (*env)->GetStringUTFChars(env, reason, 0);
LOGD("notifying call %s/%s\n", native_call, native_reason);
re_thread_enter();
(void)call_notify_sipfrag(call, native_code, native_reason);
re_thread_leave();
(*env)->ReleaseStringUTFChars(env, javaCall, native_call);
(*env)->ReleaseStringUTFChars(env, reason, native_reason);
return;
}
JNIEXPORT void JNICALL
Java_com_tutpro_baresip_Api_call_1start_1audio(JNIEnv *env, jobject thiz, jstring javaCall) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0); const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
struct call *call = (struct call *)strtoul(native_call, NULL, 10); struct call *call = (struct call *)strtoul(native_call, NULL, 10);
LOGD("starting audio of call %s\n", native_call); LOGD("starting audio of call %s\n", native_call);
@@ -949,7 +996,7 @@ Java_com_tutpro_baresip_MainActivity_call_1start_1audio(JNIEnv *env, jobject thi
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_tutpro_baresip_MainActivity_call_1stop_1audio(JNIEnv *env, jobject thiz, jstring javaCall) { Java_com_tutpro_baresip_Api_call_1stop_1audio(JNIEnv *env, jobject thiz, jstring javaCall) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0); const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
struct call *call = (struct call *)strtoul(native_call, NULL, 10); struct call *call = (struct call *)strtoul(native_call, NULL, 10);
LOGD("stopping audio of call %s\n", native_call); LOGD("stopping audio of call %s\n", native_call);
@@ -962,8 +1009,7 @@ Java_com_tutpro_baresip_MainActivity_call_1stop_1audio(JNIEnv *env, jobject thiz
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_MainActivity_call_1hold(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_call_1hold(JNIEnv *env, jobject thiz, jstring javaCall) {
jstring javaCall) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0); const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
struct call *call = (struct call *) strtoul(native_call, NULL, 10); struct call *call = (struct call *) strtoul(native_call, NULL, 10);
int ret; int ret;
@@ -979,7 +1025,7 @@ Java_com_tutpro_baresip_MainActivity_call_1hold(JNIEnv *env, jobject thiz,
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_MainActivity_call_1unhold(JNIEnv *env, jobject thiz, jstring javaCall) { Java_com_tutpro_baresip_Api_call_1unhold(JNIEnv *env, jobject thiz, jstring javaCall) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0); const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
struct call *call = (struct call *) strtoul(native_call, NULL, 10); struct call *call = (struct call *) strtoul(native_call, NULL, 10);
int ret; int ret;
@@ -994,48 +1040,9 @@ Java_com_tutpro_baresip_MainActivity_call_1unhold(JNIEnv *env, jobject thiz, jst
return ret; return ret;
} }
JNIEXPORT void JNICALL
Java_com_tutpro_baresip_MainActivity_ua_1hangup(JNIEnv *env, jobject thiz,
jstring javaUA, jstring javaCall, jint code,
jstring reason) {
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
const uint16_t native_code = code;
const char *native_reason = (*env)->GetStringUTFChars(env, reason, 0);
LOGD("hanging up call %s/%s\n", native_ua, native_call);
re_thread_enter();
if (strlen(native_reason) == 0)
ua_hangup(ua, call, native_code, NULL);
else
ua_hangup(ua, call, native_code, native_reason);
re_thread_leave();
(*env)->ReleaseStringUTFChars(env, javaUA, native_ua);
(*env)->ReleaseStringUTFChars(env, javaCall, native_call);
(*env)->ReleaseStringUTFChars(env, reason, native_reason);
return;
}
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_MainActivity_call_1send_1digit(JNIEnv *env, jobject thiz, Java_com_tutpro_baresip_Api_message_1send(JNIEnv *env, jobject thiz, jstring javaUA,
jstring javaCall, jchar digit) { jstring javaPeer, jstring javaMsg, jstring javaTime) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
const uint16_t native_digit = digit;
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
LOGD("sending DTMF digit '%c' to call %s\n", (char)native_digit, native_call);
re_thread_enter();
int res = call_send_digit(call, (char)native_digit);
if (!res) res = call_send_digit(call, KEYCODE_REL);
re_thread_leave();
(*env)->ReleaseStringUTFChars(env, javaCall, native_call);
return res;
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_ChatActivity_message_1send(JNIEnv *env, jobject thiz, jstring javaUA,
jstring javaPeer, jstring javaMsg,
jstring javaTime) {
struct ua *ua; struct ua *ua;
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0); const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
const char *native_peer = (*env)->GetStringUTFChars(env, javaPeer, 0); const char *native_peer = (*env)->GetStringUTFChars(env, javaPeer, 0);
@@ -1055,24 +1062,8 @@ Java_com_tutpro_baresip_ChatActivity_message_1send(JNIEnv *env, jobject thiz, js
return err; return err;
} }
JNIEXPORT void JNICALL
Java_com_tutpro_baresip_MainActivity_call_1notify_1sipfrag(JNIEnv *env, jobject thiz,
jstring javaCall, jint code, jstring reason) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
const uint16_t native_code = code;
const char *native_reason = (*env)->GetStringUTFChars(env, reason, 0);
LOGD("notifying call %s/%s\n", native_call, native_reason);
re_thread_enter();
(void)call_notify_sipfrag(call, native_code, native_reason);
re_thread_leave();
(*env)->ReleaseStringUTFChars(env, javaCall, native_call);
(*env)->ReleaseStringUTFChars(env, reason, native_reason);
return;
}
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_MainActivity_reload_1config(JNIEnv *env, jobject thiz) { Java_com_tutpro_baresip_Api_reload_1config(JNIEnv *env, jobject thiz) {
int err; int err;
err = conf_configure(); err = conf_configure();
if (err) { if (err) {
@@ -116,28 +116,28 @@ class Account(val accp: String) {
fun accounts(): ArrayList<Account> { fun accounts(): ArrayList<Account> {
val res = ArrayList<Account>() val res = ArrayList<Account>()
for (ua in MainActivity.uas) { for (ua in UserAgent.uas()) {
res.add(ua.account) res.add(ua.account)
} }
return res return res
} }
fun find(uas: ArrayList<UserAgent>, accp: String): Account? { fun find(accp: String): Account? {
for (ua in uas) { for (ua in UserAgent.uas()) {
if (ua.account.accp == accp) return ua.account if (ua.account.accp == accp) return ua.account
} }
return null return null
} }
fun findUa(aor: String): UserAgent? { fun findUa(aor: String): UserAgent? {
for (ua in MainActivity.uas) { for (ua in UserAgent.uas()) {
if (ua.account.aor == aor) return ua if (ua.account.aor == aor) return ua
} }
return null return null
} }
fun exists(uas: ArrayList<UserAgent>, aor: String): Boolean { fun exists(aor: String): Boolean {
for (ua in uas) { for (ua in UserAgent.uas()) {
if (ua.account.aor == aor) return true if (ua.account.aor == aor) return true
} }
return false return false
@@ -155,7 +155,6 @@ external fun account_auth_pass(acc: String): String
external fun account_set_auth_pass(acc: String, pass: String): Int external fun account_set_auth_pass(acc: String, pass: String): Int
external fun account_outbound(acc: String, ix: Int): String external fun account_outbound(acc: String, ix: Int): String
external fun account_set_outbound(acc: String, ob: String, ix: Int): Int external fun account_set_outbound(acc: String, ob: String, ix: Int): Int
external fun account_sipnat(acc: String): String
external fun account_set_sipnat(acc: String, sipnat: String): Int external fun account_set_sipnat(acc: String, sipnat: String): Int
external fun account_audio_codec(acc: String, ix: Int): String external fun account_audio_codec(acc: String, ix: Int): String
external fun account_regint(acc: String): Int external fun account_regint(acc: String): Int
@@ -32,9 +32,9 @@ class AccountActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_account) setContentView(R.layout.activity_account)
acc = Account.find(MainActivity.uas, intent.extras.getString("accp"))!! acc = Account.find(intent.extras.getString("accp"))!!
aor = acc.aor aor = acc.aor
uaIndex = UserAgent.findAorIndex(MainActivity.uas, aor)!! uaIndex = UserAgent.findAorIndex(UserAgent.uas(), aor)!!
setTitle(aor.replace("sip:", "")) setTitle(aor.replace("sip:", ""))
@@ -220,7 +220,7 @@ class AccountActivity : AppCompatActivity() {
if (acc.regint != 3600) newRegint = 3600 if (acc.regint != 3600) newRegint = 3600
} else { } else {
if (acc.regint != 0) { if (acc.regint != 0) {
UserAgent.ua_unregister(MainActivity.uas[uaIndex].uap) Api.ua_unregister(UserAgent.uas()[uaIndex].uap)
newRegint = 0 newRegint = 0
} }
} }
@@ -285,20 +285,18 @@ class AccountActivity : AppCompatActivity() {
if (save) { if (save) {
AccountsActivity.saveAccounts() AccountsActivity.saveAccounts()
if (UserAgent.ua_update_account(MainActivity.uas[uaIndex].uap) != 0) if (Api.ua_update_account(UserAgent.uas()[uaIndex].uap) != 0)
Log.e("Baresip", "Failed to update UA with AoR $aor") Log.e("Baresip", "Failed to update UA with AoR $aor")
} }
if (regCheck.isChecked) UserAgent.ua_register(MainActivity.uas[uaIndex].uap) if (regCheck.isChecked) Api.ua_register(UserAgent.uas()[uaIndex].uap)
setResult(RESULT_OK, intent)
finish() finish()
return true return true
} else if (item.itemId == android.R.id.home) { } else if (item.itemId == android.R.id.home) {
Log.d("Baresip", "Back array was pressed at Account") Log.d("Baresip", "Back array was pressed at Account")
setResult(RESULT_CANCELED, intent)
finish() finish()
return true return true
@@ -1,5 +1,6 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
@@ -28,22 +29,21 @@ class AccountListAdapter(private val cxt: Context, private val rows: ArrayList<A
aorView.setOnClickListener { _ -> aorView.setOnClickListener { _ ->
val i = Intent(cxt, AccountActivity::class.java) val i = Intent(cxt, AccountActivity::class.java)
val b = Bundle() val b = Bundle()
b.putString("accp", MainActivity.uas[position].account.accp) b.putString("accp", UserAgent.uas()[position].account.accp)
i.putExtras(b) i.putExtras(b)
cxt.startActivity(i) (cxt as Activity).startActivity(i)
} }
val ua = UserAgent.uas()[position]
val actionView = rowView.findViewById(R.id.action) as ImageButton val actionView = rowView.findViewById(R.id.action) as ImageButton
actionView.setImageResource(row.action) actionView.setImageResource(row.action)
actionView.setOnClickListener { _ -> actionView.setOnClickListener { _ ->
Log.d("Baresip", "Delete button clicked") Log.d("Baresip", "Delete button clicked")
val deleteDialog = AlertDialog.Builder(cxt) val deleteDialog = AlertDialog.Builder(cxt)
deleteDialog.setMessage("Do you want to delete account ${MainActivity.uas[position].account.aor}?") deleteDialog.setMessage("Do you want to delete account ${ua.account.aor}?")
deleteDialog.setPositiveButton("Delete") { dialog, _ -> deleteDialog.setPositiveButton("Delete") { dialog, _ ->
MainActivity.uas[position].destroy() UserAgent.remove(ua)
MainActivity.uas.remove(MainActivity.uas[position])
MainActivity.images.removeAt(position)
AccountsActivity.saveAccounts()
AccountsActivity.generateAccounts() AccountsActivity.generateAccounts()
AccountsActivity.saveAccounts()
this.notifyDataSetChanged() this.notifyDataSetChanged()
dialog.dismiss() dialog.dismiss()
} }
@@ -13,17 +13,20 @@ import java.util.ArrayList
class AccountsActivity : AppCompatActivity() { class AccountsActivity : AppCompatActivity() {
internal var aor: String = ""
internal lateinit var alAdapter: AccountListAdapter internal lateinit var alAdapter: AccountListAdapter
internal var aor = ""
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_accounts) setContentView(R.layout.activity_accounts)
val listview = findViewById(R.id.accounts) as ListView filesPath = applicationContext.filesDir.absolutePath
val listView = findViewById(R.id.accounts) as ListView
generateAccounts() generateAccounts()
alAdapter = AccountListAdapter(this, accounts) alAdapter = AccountListAdapter(this, accounts)
listview.adapter = alAdapter listView.adapter = alAdapter
val addAccountButton = findViewById(R.id.addAccount) as ImageButton val addAccountButton = findViewById(R.id.addAccount) as ImageButton
val newAorView = findViewById(R.id.newAor) as EditText val newAorView = findViewById(R.id.newAor) as EditText
@@ -34,7 +37,7 @@ class AccountsActivity : AppCompatActivity() {
Log.e("Baresip", "Invalid SIP Address of Record $aor") Log.e("Baresip", "Invalid SIP Address of Record $aor")
Utils.alertView(this, "Notice", Utils.alertView(this, "Notice",
"Invalid SIP Address of Record: $aor") "Invalid SIP Address of Record: $aor")
} else if (Account.exists(MainActivity.uas, aor)) { } else if (Account.exists(aor)) {
Log.e("Baresip", "Account $aor already exists") Log.e("Baresip", "Account $aor already exists")
} else { } else {
val ua = UserAgent.uaAlloc("<$aor>;regq=0.5;pubint=0;regint=0") val ua = UserAgent.uaAlloc("<$aor>;regq=0.5;pubint=0;regint=0")
@@ -46,8 +49,7 @@ class AccountsActivity : AppCompatActivity() {
newAorView.setText("") newAorView.setText("")
newAorView.hint = "SIP URI user@domain" newAorView.hint = "SIP URI user@domain"
newAorView.clearFocus() newAorView.clearFocus()
MainActivity.uas.add(ua) UserAgent.add(ua, R.drawable.dot_yellow)
MainActivity.images.add(R.drawable.dot_yellow)
generateAccounts() generateAccounts()
alAdapter.notifyDataSetChanged() alAdapter.notifyDataSetChanged()
saveAccounts() saveAccounts()
@@ -56,10 +58,18 @@ class AccountsActivity : AppCompatActivity() {
b.putString("accp", ua.account.accp) b.putString("accp", ua.account.accp)
i.putExtras(b) i.putExtras(b)
startActivity(i) startActivity(i)
// recreate()
} }
} }
} }
val accp = intent.extras.getString("accp")
if (accp != "") {
val i = Intent(this, AccountActivity::class.java)
val b = Bundle()
b.putString("accp", accp)
i.putExtras(b)
startActivity(i)
}
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -76,11 +86,13 @@ class AccountsActivity : AppCompatActivity() {
companion object { companion object {
internal var filesPath = ""
var accounts = ArrayList<AccountRow>() var accounts = ArrayList<AccountRow>()
fun generateAccounts() { fun generateAccounts() {
accounts.clear() accounts.clear()
for (ua in MainActivity.uas) for (ua in UserAgent.uas())
accounts.add(AccountRow(ua.account.aor.replace("sip:", ""), accounts.add(AccountRow(ua.account.aor.replace("sip:", ""),
R.drawable.action_remove)) R.drawable.action_remove))
} }
@@ -88,9 +100,8 @@ class AccountsActivity : AppCompatActivity() {
fun saveAccounts() { fun saveAccounts() {
var accounts = "" var accounts = ""
for (a in Account.accounts()) accounts = accounts + a.print() + "\n" for (a in Account.accounts()) accounts = accounts + a.print() + "\n"
val path = MainActivity.filesPath + "/accounts" Utils.putFileContents(File(filesPath + "/accounts"), accounts)
Utils.putFileContents(File(path), accounts) // Log.d("Baresip", "Saved accounts '${accounts}' to '${filesPath}/accounts")
// Log.d("Baresip", "Saved accounts '${accounts}' to '$path")
} }
} }
@@ -3,7 +3,28 @@ package com.tutpro.baresip
object Api { object Api {
external fun audio_codecs(): String external fun audio_codecs(): String
external fun uag_current_set(uap: String)
external fun ua_account(ua: String): String
external fun ua_alloc(uri: String): String
external fun ua_update_account(ua: String): Int
external fun ua_destroy(ua: String)
external fun ua_register(ua: String): Int
external fun ua_isregistered(ua: String): Boolean
external fun ua_unregister(ua: String)
external fun ua_connect(uap: String, peer_uri: String): String
external fun ua_hangup(uap: String, callp: String, code: Int, reason: String)
external fun ua_call_alloc(uap: String, xcallp: String): String
external fun ua_answer(uap: String, callp: String)
external fun call_hold(callp: String): Int
external fun call_unhold(callp: String): Int
external fun call_connect(callp: String, peer_uri: String): Int
external fun call_peeruri(callp: String): String external fun call_peeruri(callp: String): String
external fun call_send_digit(callp: String, digit: Char): Int
external fun call_notify_sipfrag(callp: String, code: Int, reason: String)
external fun call_start_audio(callp: String)
external fun call_stop_audio(callp: String)
external fun message_send(uap: String, peer_uri: String, message: String, time: String): Int
external fun reload_config(): Int
external fun cmd_exec(cmd: String): Int external fun cmd_exec(cmd: String): Int
external fun contact_add(contact: String) external fun contact_add(contact: String)
external fun contacts_remove() external fun contacts_remove()
@@ -1,11 +1,10 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.annotation.TargetApi
import android.app.* import android.app.*
import android.app.Notification.VISIBILITY_PUBLIC import android.app.Notification.VISIBILITY_PUBLIC
import android.content.BroadcastReceiver import android.content.*
import android.content.Context import android.media.*
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.NetworkInfo import android.net.NetworkInfo
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
@@ -22,11 +21,14 @@ import android.os.Build
import android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE import android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.io.InputStream import java.io.InputStream
import java.util.*
class BaresipService: Service() { class BaresipService: Service() {
private val LOG_TAG = "Baresip Service" private val LOG_TAG = "Baresip Service"
internal lateinit var intent: Intent internal lateinit var intent: Intent
internal lateinit var am: AudioManager
internal lateinit var rt: Ringtone
internal lateinit var nm: NotificationManager internal lateinit var nm: NotificationManager
internal lateinit var snb: NotificationCompat.Builder internal lateinit var snb: NotificationCompat.Builder
internal lateinit var npi: PendingIntent internal lateinit var npi: PendingIntent
@@ -34,6 +36,10 @@ class BaresipService: Service() {
internal lateinit var wl: PowerManager.WakeLock internal lateinit var wl: PowerManager.WakeLock
internal lateinit var fl: WifiManager.WifiLock internal lateinit var fl: WifiManager.WifiLock
internal var rtTimer: Timer? = null
internal var filesPath = ""
internal var restarting = false
override fun onCreate() { override fun onCreate() {
Log.d(LOG_TAG, "At onCreate") Log.d(LOG_TAG, "At onCreate")
@@ -41,6 +47,13 @@ class BaresipService: Service() {
intent = Intent("com.tutpro.baresip.EVENT") intent = Intent("com.tutpro.baresip.EVENT")
intent.setPackage("com.tutpro.baresip") intent.setPackage("com.tutpro.baresip")
filesPath = applicationContext.filesDir.absolutePath
am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val rtUri = RingtoneManager.getActualDefaultRingtoneUri(applicationContext,
RingtoneManager.TYPE_RINGTONE)
rt = RingtoneManager.getRingtone(applicationContext, rtUri)
nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
createNotificationChannels() createNotificationChannels()
snb = NotificationCompat.Builder(this, DEFAULT_CHANNEL_ID) snb = NotificationCompat.Builder(this, DEFAULT_CHANNEL_ID)
@@ -59,7 +72,7 @@ class BaresipService: Service() {
if (isConnected) { if (isConnected) {
Log.d(LOG_TAG, "Network is connected/connecting") Log.d(LOG_TAG, "Network is connected/connecting")
if (disconnected) { if (disconnected) {
UserAgent.register(MainActivity.uas) UserAgent.register(uas)
disconnected = false disconnected = false
} }
} else { } else {
@@ -103,22 +116,21 @@ class BaresipService: Service() {
val assets = arrayOf("accounts", "contacts", "config", "busy.wav", "callwaiting.wav", val assets = arrayOf("accounts", "contacts", "config", "busy.wav", "callwaiting.wav",
"error.wav", "notfound.wav", "ring.wav", "ringback.wav") "error.wav", "notfound.wav", "ring.wav", "ringback.wav")
val path = applicationContext.filesDir.path var file = File(filesPath)
var file = File(path)
if (!file.exists()) { if (!file.exists()) {
Log.d(LOG_TAG, "Creating baresip directory") Log.d(LOG_TAG, "Creating baresip directory")
try { try {
File(path).mkdirs() File(filesPath).mkdirs()
} catch (e: Error) { } catch (e: Error) {
Log.e(LOG_TAG, "Failed to create directory: " + e.toString()) Log.e(LOG_TAG, "Failed to create directory: " + e.toString())
} }
} }
for (a in assets) { for (a in assets) {
file = File("$path/$a") file = File("${filesPath}/$a")
if (!file.exists()) { if (!file.exists()) {
Log.d(LOG_TAG, "Copying asset $a") Log.d(LOG_TAG, "Copying asset $a")
Utils.copyAssetToFile(applicationContext, a, "$path/$a") Utils.copyAssetToFile(applicationContext, a, "$filesPath/$a")
} else { } else {
Log.d(LOG_TAG, "Asset $a already copied") Log.d(LOG_TAG, "Asset $a already copied")
if (a == "config") { if (a == "config") {
@@ -144,8 +156,8 @@ class BaresipService: Service() {
} }
wl.acquire() wl.acquire()
Thread(Runnable { baresipStart(path) }).start() Thread(Runnable { baresipStart(filesPath) }).start()
BaresipService.IS_SERVICE_RUNNING = true BaresipService.isServiceRunning = true
registerReceiver(nr, IntentFilter("android.net.conn.CONNECTIVITY_CHANGE")) registerReceiver(nr, IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"))
showStatusNotification() showStatusNotification()
} }
@@ -159,7 +171,7 @@ class BaresipService: Service() {
} }
"Kill" -> { "Kill" -> {
RESTARTING = true restarting = true
stop() stop()
} }
} }
@@ -175,7 +187,7 @@ class BaresipService: Service() {
Log.d(LOG_TAG, "In onDestroy") Log.d(LOG_TAG, "In onDestroy")
super.onDestroy() super.onDestroy()
Log.i(LOG_TAG, "Restart baresip killed by Android") Log.i(LOG_TAG, "Restart baresip killed by Android")
RESTARTING = true restarting = true
stop() stop()
} }
@@ -208,14 +220,13 @@ class BaresipService: Service() {
fun uaAdd(uap: String) { fun uaAdd(uap: String) {
Log.d(LOG_TAG, "addUA at BaresipService") Log.d(LOG_TAG, "addUA at BaresipService")
val ua = UserAgent(uap) val ua = UserAgent(uap)
MainActivity.uas.add(ua) uas.add(ua)
if (UserAgent.ua_isregistered(uap)) { if (Api.ua_isregistered(uap)) {
Log.d(LOG_TAG, "Ua ${ua.account.aor} is registered") Log.d(LOG_TAG, "Ua ${ua.account.aor} is registered")
MainActivity.images.add(R.drawable.dot_green) status.add(R.drawable.dot_green)
} else { } else {
Log.d(LOG_TAG, "Ua ${ua.account.aor} is NOT registered") Log.d(LOG_TAG, "Ua ${ua.account.aor} is NOT registered")
MainActivity.images.add(R.drawable.dot_yellow) status.add(R.drawable.dot_yellow)
// ua.register()
} }
val intent = Intent("service event") val intent = Intent("service event")
intent.putExtra("event", "ua added") intent.putExtra("event", "ua added")
@@ -226,54 +237,69 @@ class BaresipService: Service() {
@Keep @Keep
fun uaEvent(event: String, uap: String, callp: String) { fun uaEvent(event: String, uap: String, callp: String) {
Log.d(LOG_TAG, "updateStatus got event $event/$uap/$callp") Log.d(LOG_TAG, "uaEvent got event $event/$uap/$callp")
if (!IS_SERVICE_RUNNING) return if (!isServiceRunning) return
if (event == "exit") if (event == "exit")
return return
val ua = UserAgent.find(MainActivity.uas, uap) val ua = UserAgent.find(uap)
if (ua == null) { if (ua == null) {
Log.w(LOG_TAG, "updateStatus did not find ua $uap") Log.w(LOG_TAG, "uaEvent did not find ua $uap")
return return
} }
val aor = ua.account.aor val aor = ua.account.aor
for (account_index in MainActivity.uas.indices) { var newEvent: String? = null
if (MainActivity.uas[account_index].account.aor == aor) { val ev = event.split(",")
when (event) { for (account_index in uas.indices) {
if (uas[account_index].account.aor == aor) {
when (ev[0]) {
"registering" -> { "registering" -> {
return return
} }
"registered" -> { "registered" -> {
if (ua.account.regint == 0) if (ua.account.regint == 0)
MainActivity.images[account_index] = R.drawable.dot_yellow status[account_index] = R.drawable.dot_yellow
else else
MainActivity.images[account_index] = R.drawable.dot_green status[account_index] = R.drawable.dot_green
updateStatusNotification() updateStatusNotification()
} }
"registering failed" -> { "registering failed" -> {
MainActivity.images[account_index] = R.drawable.dot_red status[account_index] = R.drawable.dot_red
updateStatusNotification() updateStatusNotification()
} }
"unregistering" -> { "unregistering" -> {
MainActivity.images[account_index] = R.drawable.dot_yellow status[account_index] = R.drawable.dot_yellow
updateStatusNotification() updateStatusNotification()
} }
"call incoming" -> { "call incoming" -> {
if (!Utils.isVisible()) { val peerUri = Api.call_peeruri(callp)
if (Call.calls().size > 0) {
Log.i(LOG_TAG, "Auto-rejecting incoming call $uap/$callp/$peerUri")
Api.ua_hangup(uap, callp, 486, "Busy Here")
CallHistory.add(CallHistory(aor, peerUri, "in", false))
CallHistory.save(filesPath)
ua.account.missedCalls = true
newEvent = "call rejected"
} else {
Log.d(LOG_TAG, "Incoming call $uap/$callp/$peerUri")
calls.add(Call(callp, ua, peerUri, "in", "incoming",
Utils.dtmfWatcher(callp)))
startRinging()
}
if ((newEvent == null) && !Utils.isVisible()) {
val cnb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) val cnb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
val caller = Utils.friendlyUri(ContactsActivity.contactName(peerUri),
Utils.aorDomain(aor))
val title = "Incoming call from $caller"
cnb.setSmallIcon(R.drawable.ic_stat) cnb.setSmallIcon(R.drawable.ic_stat)
.setColor(0x0ca1fd) .setColor(0x0ca1fd)
.setContentIntent(npi) .setContentIntent(npi)
.setAutoCancel(true) .setAutoCancel(true)
.setContentTitle("Call from ${ContactsActivity.contactName( .setContentTitle(title)
Api.call_peeruri(callp))}")
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
cnb.setVibrate(LongArray(0)) cnb.setVibrate(LongArray(0))
.setVisibility(VISIBILITY_PRIVATE) .setVisibility(VISIBILITY_PRIVATE)
.setPriority(Notification.PRIORITY_HIGH) .setPriority(Notification.PRIORITY_HIGH)
} }
/* val view = RemoteViews(getPackageName(), R.layout.call_notification)
view.setTextViewText(R.id.callFrom, "Call from ${Api.call_peeruri(callp)}")
cnb.setContent(view) */
val answerIntent = Intent(this, MainActivity::class.java) val answerIntent = Intent(this, MainActivity::class.java)
answerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or answerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP) Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
@@ -288,22 +314,114 @@ class BaresipService: Service() {
rejectIntent.putExtra("callp", callp) rejectIntent.putExtra("callp", callp)
val rejectPendingIntent = PendingIntent.getActivity(this, val rejectPendingIntent = PendingIntent.getActivity(this,
1, rejectIntent, PendingIntent.FLAG_UPDATE_CURRENT) 1, rejectIntent, PendingIntent.FLAG_UPDATE_CURRENT)
/* view.setOnClickPendingIntent(R.id.answerButton, answerPendingIntent)
view.setOnClickPendingIntent(R.id.rejectButton, rejectPendingIntent)
cnb.setCustomBigContentView(view) */
cnb.addAction(R.drawable.ic_stat, "Answer", answerPendingIntent) cnb.addAction(R.drawable.ic_stat, "Answer", answerPendingIntent)
cnb.addAction(R.drawable.ic_stat, "Reject", rejectPendingIntent) cnb.addAction(R.drawable.ic_stat, "Reject", rejectPendingIntent)
nm.notify(CALL_NOTIFICATION_ID, cnb.build()) nm.notify(CALL_NOTIFICATION_ID, cnb.build())
} }
} }
"call established", "call closed" -> { "call established" -> {
nm.cancel(CALL_NOTIFICATION_ID) nm.cancel(CALL_NOTIFICATION_ID)
val call = Call.find(callp)
if (call == null) {
Log.w(LOG_TAG, "Established AoR $aor call $callp is not found")
return
}
Log.d("Baresip", "AoR $aor call $callp established")
call.status = "connected"
call.onhold = false
call.security = R.drawable.box_red
CallHistory.add(CallHistory(aor, call.peerURI, call.dir, true))
CallHistory.save(filesPath)
call.hasHistory = true
if (rtTimer != null) {
rtTimer!!.cancel()
rtTimer = null
if (rt.isPlaying) rt.stop()
}
am.mode = AudioManager.MODE_IN_COMMUNICATION
requestAudioFocus( AudioManager.STREAM_VOICE_CALL)
am.isSpeakerphoneOn = false
}
"call transfer" -> {
val call = Call.find(callp)
if (call == null) {
Log.d("Baresip","AoR $aor call $callp to be transferred is not found")
return
}
if (!Utils.isVisible()) {
val cnb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
val target = Utils.friendlyUri(ContactsActivity.contactName(ev[1]),
Utils.aorDomain(aor))
val title = "Call transfer request to $target"
cnb.setSmallIcon(R.drawable.ic_stat)
.setColor(0x0ca1fd)
.setContentIntent(npi)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setContentTitle(title)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
cnb.setVibrate(LongArray(0))
.setVisibility(VISIBILITY_PRIVATE)
.setPriority(Notification.PRIORITY_HIGH)
}
val acceptIntent = Intent(this, MainActivity::class.java)
acceptIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
acceptIntent.putExtra("action", "transfer")
acceptIntent.putExtra("uap", uap)
acceptIntent.putExtra("callp", callp)
acceptIntent.putExtra("uri", ev[1])
val acceptPendingIntent = PendingIntent.getActivity(this,
0, acceptIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val rejectIntent = Intent(this, MainActivity::class.java)
rejectIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP)
rejectIntent.putExtra("action", "deny")
rejectIntent.putExtra("callp", callp)
val rejectPendingIntent = PendingIntent.getActivity(this,
1, rejectIntent, PendingIntent.FLAG_UPDATE_CURRENT)
cnb.addAction(R.drawable.ic_stat, "Accept", acceptPendingIntent)
cnb.addAction(R.drawable.ic_stat, "Deny", rejectPendingIntent)
nm.notify(CALL_NOTIFICATION_ID, cnb.build())
return
}
}
"call closed" -> {
nm.cancel(CALL_NOTIFICATION_ID)
val call = Call.find(callp)
if (call == null) {
Log.d("Baresip","AoR $aor call $callp that is closed is not found")
return
}
Log.d("Baresip", "AoR $aor call $callp is closed")
stopRinging()
calls.remove(call)
if (!call.hasHistory) {
CallHistory.add(CallHistory(aor, call.peerURI, call.dir, false))
CallHistory.save(filesPath)
if (call.dir == "in") ua.account.missedCalls = true
}
if (Call.calls().size == 0) {
am.mode = AudioManager.MODE_NORMAL
} else {
val uaCalls = Call.uaCalls(ua, "")
if (uaCalls.size > 0)
Api.call_start_audio(uaCalls[uaCalls.size - 1].callp)
}
if (am.isSpeakerphoneOn) am.isSpeakerphoneOn = false
if (audioFocused) abandonAudioFocus()
}
"transfer failed" -> {
Log.d(LOG_TAG, "AoR $aor hanging up call $callp with ${ev[1]}")
Api.ua_hangup(uap, callp, 0, "")
return
} }
} }
} }
} }
if (newEvent == null) newEvent = event
val intent = Intent("service event") val intent = Intent("service event")
intent.putExtra("event", event) intent.putExtra("event", newEvent)
intent.putExtra("params", arrayListOf(uap, callp)) intent.putExtra("params", arrayListOf(uap, callp))
LocalBroadcastManager.getInstance(this).sendBroadcast(intent) LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
} }
@@ -316,25 +434,29 @@ class BaresipService: Service() {
} catch (e: Exception) { } catch (e: Exception) {
Log.e(LOG_TAG, "UTF-8 decode failed") Log.e(LOG_TAG, "UTF-8 decode failed")
} }
Log.d(LOG_TAG, "Message event $uap/$peer/$s") Log.d(LOG_TAG, "Message event for $uap from $peer")
val ua = UserAgent.find(uap)
if (ua == null) {
Log.w(LOG_TAG, "messageEvent did not find ua $uap")
return
}
val timeStamp = System.currentTimeMillis().toString() val timeStamp = System.currentTimeMillis().toString()
if (!Utils.isVisible()) { if (!Utils.isVisible()) {
val cnb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) val cnb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
val sender = Utils.friendlyUri(ContactsActivity.contactName(peer),
Utils.aorDomain(ua.account.aor))
cnb.setSmallIcon(R.drawable.ic_stat) cnb.setSmallIcon(R.drawable.ic_stat)
.setColor(0x0ca1fd) .setColor(0x0ca1fd)
.setContentIntent(npi) .setContentIntent(npi)
.setDefaults(Notification.DEFAULT_SOUND) .setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true) .setAutoCancel(true)
.setContentTitle("Message from ${ContactsActivity.contactName(peer)}") .setContentTitle("Message from $sender")
.setContentText(s) .setContentText(s)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
cnb.setVibrate(LongArray(0)) cnb.setVibrate(LongArray(0))
.setVisibility(VISIBILITY_PRIVATE) .setVisibility(VISIBILITY_PRIVATE)
.setPriority(Notification.PRIORITY_HIGH) .setPriority(Notification.PRIORITY_HIGH)
} }
/* val view = RemoteViews(getPackageName(), R.layout.call_notification)
view.setTextViewText(R.id.callFrom, "Call from ${Api.call_peeruri(callp)}")
cnb.setContent(view) */
val replyIntent = Intent(this, MainActivity::class.java) val replyIntent = Intent(this, MainActivity::class.java)
replyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or replyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP) Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
@@ -359,9 +481,6 @@ class BaresipService: Service() {
deleteIntent.putExtra("time", timeStamp) deleteIntent.putExtra("time", timeStamp)
val deletePendingIntent = PendingIntent.getActivity(this, val deletePendingIntent = PendingIntent.getActivity(this,
2, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT) 2, deleteIntent, PendingIntent.FLAG_UPDATE_CURRENT)
/* view.setOnClickPendingIntent(R.id.answerButton, answerPendingIntent)
view.setOnClickPendingIntent(R.id.rejectButton, rejectPendingIntent)
cnb.setCustomBigContentView(view) */
cnb.addAction(R.drawable.ic_stat, "Reply", replyPendingIntent) cnb.addAction(R.drawable.ic_stat, "Reply", replyPendingIntent)
cnb.addAction(R.drawable.ic_stat, "Save", savePendingIntent) cnb.addAction(R.drawable.ic_stat, "Save", savePendingIntent)
cnb.addAction(R.drawable.ic_stat, "Delete", deletePendingIntent) cnb.addAction(R.drawable.ic_stat, "Delete", deletePendingIntent)
@@ -385,28 +504,28 @@ class BaresipService: Service() {
@Keep @Keep
fun stopped() { fun stopped() {
Log.d(LOG_TAG, "got event 'stopped'") Log.d(LOG_TAG, "got event 'stopped'")
IS_SERVICE_RUNNING = false isServiceRunning = false
val intent = Intent("service event") val intent = Intent("service event")
intent.putExtra("event", "stopped") intent.putExtra("event", "stopped")
intent.putExtra("params", arrayListOf<String>()) intent.putExtra("params", arrayListOf<String>())
LocalBroadcastManager.getInstance(this).sendBroadcast(intent) LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
stopForeground(true) stopForeground(true)
stopSelf() stopSelf()
if (RESTARTING) restart() if (restarting) restart()
} }
private fun updateStatusNotification() { private fun updateStatusNotification() {
val contentView = RemoteViews(getPackageName(), R.layout.status_notification) val contentView = RemoteViews(getPackageName(), R.layout.status_notification)
for (i: Int in 0 .. 5) { for (i: Int in 0 .. 5) {
val resID = resources.getIdentifier("status$i", "id", packageName) val resID = resources.getIdentifier("status$i", "id", packageName)
if (i < MainActivity.images.size) { if (i < status.size) {
contentView.setImageViewResource(resID, MainActivity.images[i]) contentView.setImageViewResource(resID, status[i])
contentView.setViewVisibility(resID, View.VISIBLE) contentView.setViewVisibility(resID, View.VISIBLE)
} else { } else {
contentView.setViewVisibility(resID, View.INVISIBLE) contentView.setViewVisibility(resID, View.INVISIBLE)
} }
} }
if (MainActivity.images.size > 4) if (status.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)
@@ -414,11 +533,78 @@ class BaresipService: Service() {
nm.notify(STATUS_NOTIFICATION_ID, snb.build()) nm.notify(STATUS_NOTIFICATION_ID, snb.build())
} }
private fun requestAudioFocus(streamType: Int) {
val res: Int
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) && (audioFocusRequest == null)) {
val playbackAttributes = AudioAttributes.Builder()
.setLegacyStreamType(streamType)
.build()
@TargetApi(26)
audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
.setAudioAttributes(playbackAttributes)
.build()
@TargetApi(26)
res = am.requestAudioFocus(audioFocusRequest)
if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d("Baresip", "Audio focus granted")
} else {
Log.d("Baresip", "Audio focus denied")
audioFocusRequest = null
}
} else {
res = am.requestAudioFocus(null, streamType,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d("Baresip", "Audio focus granted")
audioFocused = true
} else {
Log.d("Baresip", "Audio focus denied")
audioFocused = false
}
}
}
private fun abandonAudioFocus() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (audioFocusRequest != null) {
am.abandonAudioFocusRequest(audioFocusRequest)
audioFocusRequest = null
}
} else {
if (audioFocused) {
am.abandonAudioFocus(null)
audioFocused = false
}
}
}
private fun startRinging() {
am.mode = AudioManager.MODE_RINGTONE
requestAudioFocus(AudioManager.STREAM_RING)
rt.play()
rtTimer = Timer()
rtTimer!!.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
if (!rt.isPlaying()) {
rt.play()
}
}
}, 1000 * 1, 1000 * 1)
}
private fun stopRinging() {
if (rtTimer != null) {
rtTimer!!.cancel()
rtTimer = null
if (rt.isPlaying) rt.stop()
}
}
private fun stop() { private fun stop() {
MainActivity.uas.clear() uas.clear()
MainActivity.images.clear() status.clear()
MainActivity.history.clear() history.clear()
MainActivity.messages.clear() messages.clear()
try { try {
unregisterReceiver(nr) unregisterReceiver(nr)
} catch (e: Exception) { } catch (e: Exception) {
@@ -427,15 +613,15 @@ class BaresipService: Service() {
if (this::nm.isInitialized) nm.cancelAll() if (this::nm.isInitialized) nm.cancelAll()
if (this::wl.isInitialized && wl.isHeld) wl.release() if (this::wl.isInitialized && wl.isHeld) wl.release()
if (this::fl.isInitialized && fl.isHeld) fl.release() if (this::fl.isInitialized && fl.isHeld) fl.release()
if (IS_SERVICE_RUNNING) { if (isServiceRunning) {
baresipStop(forceStop) baresipStop(forceStop)
if (!forceStop) forceStop = true if (!forceStop) forceStop = true
} else if (RESTARTING) } else if (restarting)
restart() restart()
} }
private fun restart() { private fun restart() {
RESTARTING = false restarting = false
val broadcastIntent = Intent("com.tutpro.baresip.Restart") val broadcastIntent = Intent("com.tutpro.baresip.Restart")
sendBroadcast(broadcastIntent) sendBroadcast(broadcastIntent)
} }
@@ -445,16 +631,27 @@ class BaresipService: Service() {
companion object { companion object {
var IS_SERVICE_RUNNING = false
var RESTARTING = false
val STATUS_NOTIFICATION_ID = 101 val STATUS_NOTIFICATION_ID = 101
val CALL_NOTIFICATION_ID = 102 val CALL_NOTIFICATION_ID = 102
val MESSAGE_NOTIFICATION_ID = 103 val MESSAGE_NOTIFICATION_ID = 103
val DEFAULT_CHANNEL_ID = "com.tutpro.baresip.default" val DEFAULT_CHANNEL_ID = "com.tutpro.baresip.default"
val HIGH_CHANNEL_ID = "com.tutpro.baresip.high" val HIGH_CHANNEL_ID = "com.tutpro.baresip.high"
var isServiceRunning = false
var disconnected = false var disconnected = false
var forceStop = false var forceStop = false
var uas = ArrayList<UserAgent>()
var status = ArrayList<Int>()
var calls = ArrayList<Call>()
var history = ArrayList<CallHistory>()
var messages = ArrayList<Message>()
var contacts = ArrayList<Contact>()
var audioFocused = false
var audioFocusRequest: AudioFocusRequest? = null
} }
init { init {
+17 -6
View File
@@ -13,6 +13,10 @@ class Call(val callp: String, val ua: UserAgent, val peerURI: String, val dir: S
companion object { companion object {
fun calls(): ArrayList<Call> {
return BaresipService.calls
}
fun calls(calls: ArrayList<Call>, dir: 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) {
@@ -21,17 +25,24 @@ class Call(val callp: String, val ua: UserAgent, val peerURI: String, val dir: S
return result return result
} }
fun uaCalls(calls: ArrayList<Call>, ua: UserAgent, dir: String): ArrayList<Call> { fun calls(dir: String): ArrayList<Call> {
val result = ArrayList<Call>() val result = ArrayList<Call>()
for (i in calls.indices) for (c in BaresipService.calls) {
if ((calls[i].ua == ua) && ((dir == "") || calls[i].dir == dir)) result.add(calls[i]) if (c.dir == dir) result.add(c)
}
return result return result
} }
fun find(calls: ArrayList<Call>, callp: String): Call? { fun uaCalls(ua: UserAgent, dir: String): ArrayList<Call> {
for (c in calls) { val result = ArrayList<Call>()
if (c.callp == callp) return c for (c in BaresipService.calls)
if ((c.ua == ua) && ((dir == "") || c.dir == dir)) result.add(c)
return result
} }
fun find(callp: String): Call? {
for (c in BaresipService.calls)
if (c.callp == callp) return c
return null return null
} }
@@ -1,6 +1,8 @@
package com.tutpro.baresip package com.tutpro.baresip
import java.io.Serializable import android.util.Log
import java.io.*
import java.util.ArrayList
import java.util.GregorianCalendar import java.util.GregorianCalendar
class CallHistory(val aor: String, val peerURI: String, val direction: String, class CallHistory(val aor: String, val peerURI: String, val direction: String,
@@ -10,29 +12,72 @@ class CallHistory(val aor: String, val peerURI: String, val direction: String,
companion object { companion object {
fun aorHistory(history: ArrayList<CallHistory>, aor: String): Int { const val CALL_HISTORY_SIZE = 100
fun history(): ArrayList<CallHistory> {
return BaresipService.history
}
fun historySet(history: ArrayList<CallHistory>) {
BaresipService.history = history
}
fun add(history: CallHistory) {
BaresipService.history.add(history)
if (aorHistorySize(history.aor) > CALL_HISTORY_SIZE) {
var i = 0
for (h in BaresipService.history)
if (h.aor == history.aor)
break
else
i++
BaresipService.history.removeAt(i)
}
}
fun aorHistorySize(aor: String): Int {
var size = 0; var size = 0;
for (h in history) { for (h in BaresipService.history) {
if (h.aor == aor) size++ if (h.aor == aor) size++
} }
return size return size
} }
fun aorRemoveHistory(history: ArrayList<CallHistory>, aor: String) { fun aorLatestHistory(aor: String): CallHistory? {
for (h in history) { for (h in BaresipService.history.reversed())
if (h.aor == aor) {
history.remove(h)
return
}
}
}
fun aorLatestHistory(history: ArrayList<CallHistory>, aor: String): CallHistory? {
for (h in history.reversed())
if (h.aor == aor) return h if (h.aor == aor) return h
return null return null
} }
fun save(path: String) {
val file = File(path, "history")
try {
val fos = FileOutputStream(file)
val oos = ObjectOutputStream(fos)
oos.writeObject(history())
oos.close()
fos.close()
} catch (e: IOException) {
Log.w("Baresip", "OutputStream exception: " + e.toString())
e.printStackTrace()
}
}
fun restore(path: String) {
val file = File(path + "/history")
if (file.exists()) {
try {
val fis = FileInputStream(file)
val ois = ObjectInputStream(fis)
historySet(ois.readObject() as ArrayList<CallHistory>)
ois.close()
fis.close()
} catch (e: Exception) {
Log.w("Baresip", "InputStream exception: - " + e.toString())
}
}
}
} }
} }
@@ -11,12 +11,6 @@ import android.view.MenuItem
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ListView import android.widget.ListView
import android.widget.TextView import android.widget.TextView
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.ArrayList import java.util.ArrayList
@@ -120,7 +114,12 @@ class CallsActivity : AppCompatActivity() {
true true
} }
ua!!.account.missedCalls = false ua.account.missedCalls = false
}
override fun onPause() {
CallHistory.save(applicationContext.filesDir.absolutePath)
super.onPause()
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -137,8 +136,8 @@ class CallsActivity : AppCompatActivity() {
private fun aorGenerateHistory(aor: String) { private fun aorGenerateHistory(aor: String) {
uaHistory.clear() uaHistory.clear()
for (i in MainActivity.history.indices.reversed()) { for (i in CallHistory.history().indices.reversed()) {
val h = MainActivity.history[i] val h = CallHistory.history()[i]
if (h.aor == aor) { if (h.aor == aor) {
var direction: Int var direction: Int
if (h.direction == "in") if (h.direction == "in")
@@ -171,7 +170,7 @@ class CallsActivity : AppCompatActivity() {
private fun removeUaHistoryAt(i: Int) { private fun removeUaHistoryAt(i: Int) {
for (index in uaHistory[i].indexes) for (index in uaHistory[i].indexes)
MainActivity.history.removeAt(index) CallHistory.history().removeAt(index)
uaHistory.removeAt(i) uaHistory.removeAt(i)
} }
@@ -182,38 +181,4 @@ class CallsActivity : AppCompatActivity() {
now.get(Calendar.DAY_OF_MONTH) == time.get(Calendar.DAY_OF_MONTH) now.get(Calendar.DAY_OF_MONTH) == time.get(Calendar.DAY_OF_MONTH)
} }
companion object {
fun saveHistory() {
val file = File(MainActivity.filesPath, "history")
try {
val fos = FileOutputStream(file)
val oos = ObjectOutputStream(fos)
oos.writeObject(MainActivity.history)
oos.close()
fos.close()
} catch (e: IOException) {
Log.w("Baresip", "OutputStream exception: " + e.toString())
e.printStackTrace()
}
}
fun restoreHistory(path: String) {
val file = File(path + "/history")
if (file.exists()) {
try {
val fis = FileInputStream(file)
val ois = ObjectInputStream(fis)
@SuppressWarnings("unchecked")
MainActivity.history = ois.readObject() as ArrayList<CallHistory>
Log.d("Baresip", "Restored History of ${MainActivity.history.size} entries")
ois.close()
fis.close()
} catch (e: Exception) {
Log.w("Baresip", "InputStream exception: - " + e.toString())
}
}
}
}
} }
@@ -82,7 +82,7 @@ class ChatActivity : AppCompatActivity() {
startActivityForResult(i, MainActivity.CONTACT_CODE) startActivityForResult(i, MainActivity.CONTACT_CODE)
} }
DialogInterface.BUTTON_NEGATIVE -> { DialogInterface.BUTTON_NEGATIVE -> {
MainActivity.messages.remove(chatMessages[pos]) Message.messages().remove(chatMessages[pos])
chatMessages.removeAt(pos) chatMessages.removeAt(pos)
mlAdapter.notifyDataSetChanged() mlAdapter.notifyDataSetChanged()
if (chatMessages.size == 0) { if (chatMessages.size == 0) {
@@ -118,10 +118,10 @@ class ChatActivity : AppCompatActivity() {
imm.hideSoftInputFromWindow(newMessage.windowToken, 0) imm.hideSoftInputFromWindow(newMessage.windowToken, 0)
val time = System.currentTimeMillis() val time = System.currentTimeMillis()
val msg = Message(aor, peerUri, R.drawable.arrow_up_yellow, msgText, time,true) val msg = Message(aor, peerUri, R.drawable.arrow_up_yellow, msgText, time,true)
MainActivity.messages.add(msg) Message.add(msg)
chatMessages.add(msg) chatMessages.add(msg)
mlAdapter.notifyDataSetChanged() mlAdapter.notifyDataSetChanged()
if (message_send(ua!!.uap, peerUri, msgText, time.toString()) != 0) if (Api.message_send(ua.uap, peerUri, msgText, time.toString()) != 0)
Toast.makeText(getApplicationContext(), "Sending of message failed!", Toast.makeText(getApplicationContext(), "Sending of message failed!",
Toast.LENGTH_SHORT).show() Toast.LENGTH_SHORT).show()
else else
@@ -146,7 +146,7 @@ class ChatActivity : AppCompatActivity() {
} }
override fun onPause() { override fun onPause() {
ChatsActivity.saveMessages() ChatsActivity.saveMessages(applicationContext.filesDir.absolutePath)
super.onPause() super.onPause()
} }
@@ -169,7 +169,7 @@ class ChatActivity : AppCompatActivity() {
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP) Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra("action", "call") intent.putExtra("action", "call")
intent.putExtra("uap", ua.uap) intent.putExtra("uap", ua.uap)
intent.putExtra("peer", ContactsActivity.contactName(peerUri)) intent.putExtra("peer", peerUri)
startActivity(intent) startActivity(intent)
} }
} }
@@ -185,7 +185,7 @@ class ChatActivity : AppCompatActivity() {
private fun uaPeerMessages(aor: String, peerUri: String): ArrayList<Message> { private fun uaPeerMessages(aor: String, peerUri: String): ArrayList<Message> {
val res = ArrayList<Message>() val res = ArrayList<Message>()
for (m in MainActivity.messages) for (m in Message.messages())
if ((m.aor == aor) && (m.peerUri == peerUri)) res.add(m) if ((m.aor == aor) && (m.peerUri == peerUri)) res.add(m)
return res return res
} }
@@ -203,6 +203,4 @@ class ChatActivity : AppCompatActivity() {
} }
} }
external fun message_send(uap: String, peer_uri: String, message: String, time: String): Int
} }
@@ -30,6 +30,8 @@ class ChatsActivity: AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chats) setContentView(R.layout.activity_chats)
filesPath = applicationContext.filesDir.absolutePath
listView = findViewById(R.id.chats) as ListView listView = findViewById(R.id.chats) as ListView
plusButton = findViewById(R.id.plusButton) as ImageButton plusButton = findViewById(R.id.plusButton) as ImageButton
@@ -67,13 +69,13 @@ class ChatsActivity: AppCompatActivity() {
DialogInterface.BUTTON_NEGATIVE -> { DialogInterface.BUTTON_NEGATIVE -> {
val peerUri = uaMessages[pos].peerUri val peerUri = uaMessages[pos].peerUri
val msgs = ArrayList<Message>() val msgs = ArrayList<Message>()
for (m in MainActivity.messages) for (m in Message.messages())
if ((m.aor != aor) || (m.peerUri != peerUri)) if ((m.aor != aor) || (m.peerUri != peerUri))
msgs.add(m) msgs.add(m)
else else
clAdapter.remove(m) clAdapter.remove(m)
clAdapter.notifyDataSetChanged() clAdapter.notifyDataSetChanged()
MainActivity.messages = msgs Message.messages(msgs)
uaMessages = uaMessages(aor) uaMessages = uaMessages(aor)
} }
DialogInterface.BUTTON_POSITIVE -> { DialogInterface.BUTTON_POSITIVE -> {
@@ -84,8 +86,8 @@ class ChatsActivity: AppCompatActivity() {
val builder = AlertDialog.Builder(this@ChatsActivity, R.style.Theme_AppCompat) val builder = AlertDialog.Builder(this@ChatsActivity, R.style.Theme_AppCompat)
val peer = ContactsActivity.contactName(uaMessages[pos].peerUri) val peer = ContactsActivity.contactName(uaMessages[pos].peerUri)
if (peer.startsWith("sip:")) if (peer.startsWith("sip:"))
builder.setMessage("Do you want to delete chat with peer '" + builder.setMessage("Do you want to delete chat with peer " +
"${Utils.friendlyUri(peer, Utils.aorDomain(aor))}' or add peer to contacts?") "${Utils.friendlyUri(peer, Utils.aorDomain(aor))} or add peer to contacts?")
.setPositiveButton("Cancel", dialogClickListener) .setPositiveButton("Cancel", dialogClickListener)
.setNegativeButton("Delete Chat", dialogClickListener) .setNegativeButton("Delete Chat", dialogClickListener)
.setNeutralButton("Add Contact", dialogClickListener) .setNeutralButton("Add Contact", dialogClickListener)
@@ -101,7 +103,7 @@ class ChatsActivity: AppCompatActivity() {
peerUri = findViewById(R.id.peerUri) as AutoCompleteTextView peerUri = findViewById(R.id.peerUri) as AutoCompleteTextView
peerUri.threshold = 2 peerUri.threshold = 2
peerUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, peerUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
ContactsActivity.contacts.map{Contact -> Contact.name})) Contact.contacts().map{Contact -> Contact.name}))
plusButton.setOnClickListener { plusButton.setOnClickListener {
val uriText = peerUri.text.toString().trim() val uriText = peerUri.text.toString().trim()
@@ -143,7 +145,7 @@ class ChatsActivity: AppCompatActivity() {
} }
override fun onPause() { override fun onPause() {
saveMessages() saveMessages(applicationContext.filesDir.absolutePath)
super.onPause() super.onPause()
} }
@@ -171,7 +173,7 @@ class ChatsActivity: AppCompatActivity() {
private fun uaMessages(aor: String) : ArrayList<Message> { private fun uaMessages(aor: String) : ArrayList<Message> {
val res = ArrayList<Message>() val res = ArrayList<Message>()
for (m in MainActivity.messages.reversed()) { for (m in Message.messages().reversed()) {
if (m.aor != aor) continue if (m.aor != aor) continue
var found = false var found = false
for (r in res) for (r in res)
@@ -186,32 +188,34 @@ class ChatsActivity: AppCompatActivity() {
companion object { companion object {
fun saveUaMessage(aor: String, time: Long) { var filesPath = ""
for (i in MainActivity.messages.indices.reversed())
if ((MainActivity.messages[i].aor == aor) && fun saveUaMessage(aor: String, time: Long, path: String) {
(MainActivity.messages[i].timeStamp == time)) { for (i in Message.messages().indices.reversed())
MainActivity.messages[i].new = false if ((Message.messages()[i].aor == aor) &&
saveMessages() (Message.messages()[i].timeStamp == time)) {
Message.messages()[i].new = false
saveMessages(path)
return return
} }
} }
fun deleteUaMessage(aor: String, time: Long) { fun deleteUaMessage(aor: String, time: Long, path: String) {
for (i in MainActivity.messages.indices.reversed()) for (i in Message.messages().indices.reversed())
if ((MainActivity.messages[i].aor == aor) && if ((Message.messages()[i].aor == aor) &&
(MainActivity.messages[i].timeStamp == time)) { (Message.messages()[i].timeStamp == time)) {
MainActivity.messages.removeAt(i) Message.messages().removeAt(i)
saveMessages() saveMessages(path)
return return
} }
} }
fun saveMessages() { fun saveMessages(path: String = filesPath) {
val file = File(MainActivity.filesPath, "messages") val file = File(path, "messages")
try { try {
val fos = FileOutputStream(file) val fos = FileOutputStream(file)
val oos = ObjectOutputStream(fos) val oos = ObjectOutputStream(fos)
oos.writeObject(MainActivity.messages) oos.writeObject(Message.messages())
oos.close() oos.close()
fos.close() fos.close()
} catch (e: IOException) { } catch (e: IOException) {
@@ -220,15 +224,13 @@ class ChatsActivity: AppCompatActivity() {
} }
} }
fun restoreMessages(path: String) { fun restoreMessages(path: String = filesPath) {
val file = File(path, "messages") val file = File(path, "messages")
if (file.exists()) { if (file.exists()) {
try { try {
val fis = FileInputStream(file) val fis = FileInputStream(file)
val ois = ObjectInputStream(fis) val ois = ObjectInputStream(fis)
@SuppressWarnings("unchecked") Message.messages(ois.readObject() as ArrayList<Message>)
MainActivity.messages = ois.readObject() as ArrayList<Message>
Log.d("Baresip", "Restored ${MainActivity.messages.size} messages")
ois.close() ois.close()
fis.close() fis.close()
} catch (e: Exception) { } catch (e: Exception) {
@@ -236,5 +238,6 @@ class ChatsActivity: AppCompatActivity() {
} }
} }
} }
} }
} }
@@ -1,3 +1,16 @@
package com.tutpro.baresip package com.tutpro.baresip
class Contact(var name: String, var uri: String) import java.util.ArrayList
class Contact(var name: String, var uri: String) {
companion object {
const val CONTACTS_SIZE = 100
fun contacts(): ArrayList<Contact> {
return BaresipService.contacts
}
}
}
@@ -37,10 +37,10 @@ class ContactActivity : AppCompatActivity() {
} }
} else { } else {
index = intent.extras.getInt("index") index = intent.extras.getInt("index")
val name = ContactsActivity.contacts[index].name val name = Contact.contacts()[index].name
setTitle(name) setTitle(name)
nameView.setText(name) nameView.setText(name)
uriView.setText(ContactsActivity.contacts[index].uri) uriView.setText(Contact.contacts()[index].uri)
} }
} }
@@ -63,7 +63,7 @@ class ContactActivity : AppCompatActivity() {
"Invalid contact name: $newName") "Invalid contact name: $newName")
return false return false
} }
if ((new || (ContactsActivity.contacts[index].name != newName)) && if ((new || (Contact.contacts()[index].name != newName)) &&
ContactsActivity.nameExists(newName)) { ContactsActivity.nameExists(newName)) {
Utils.alertView(this, "Notice", Utils.alertView(this, "Notice",
"Contact $newName already exists") "Contact $newName already exists")
@@ -73,20 +73,27 @@ class ContactActivity : AppCompatActivity() {
var newUri = uriView.text.toString().trim() var newUri = uriView.text.toString().trim()
if (!newUri.startsWith("<")) { if (!newUri.startsWith("<")) {
if (!newUri.startsWith("sip:")) newUri = "sip:$newUri" if (!newUri.startsWith("sip:")) newUri = "sip:$newUri"
if (!Utils.checkSipUri(newUri)) { if (!Utils.checkAorUri(newUri)) {
Utils.alertView(this, "Notice","Invalid contact URI: $newUri") Utils.alertView(this, "Notice","Invalid contact URI: $newUri")
return false return false
} }
} }
if (new) { if (new) {
ContactsActivity.contacts.add(Contact(newName, newUri)) if (Contact.contacts().size >= Contact.CONTACTS_SIZE) {
Utils.alertView(this,
"Notice", "Maximum number of contacts exceeded")
return true
} else { } else {
ContactsActivity.contacts[index].uri = newUri Contact.contacts().add(Contact(newName, newUri))
ContactsActivity.contacts[index].name = newName
} }
ContactsActivity.contacts.sortBy { Contact -> Contact.name } } else {
ContactsActivity.saveContacts() Contact.contacts()[index].uri = newUri
Contact.contacts()[index].name = newName
}
Contact.contacts().sortBy { Contact -> Contact.name }
ContactsActivity.saveContacts(applicationContext.filesDir.absolutePath)
i.putExtra("name", newName) i.putExtra("name", newName)
setResult(Activity.RESULT_OK, i) setResult(Activity.RESULT_OK, i)
@@ -104,8 +111,10 @@ class ContactActivity : AppCompatActivity() {
} }
companion object { companion object {
internal var index = 0 internal var index = 0
internal var new = false internal var new = false
} }
} }
@@ -45,7 +45,7 @@ class ContactListAdapter(private val cxt: Context, private val rows: ArrayList<C
Log.e("Baresip", "onClickListener did not find AoR $aor") Log.e("Baresip", "onClickListener did not find AoR $aor")
} else { } else {
i.putExtra("uap", ua.uap) i.putExtra("uap", ua.uap)
i.putExtra("peer", ContactsActivity.contacts[pos].uri) i.putExtra("peer", Contact.contacts()[pos].uri)
(cxt as Activity).startActivity(i) (cxt as Activity).startActivity(i)
} }
} }
@@ -55,7 +55,7 @@ class ContactListAdapter(private val cxt: Context, private val rows: ArrayList<C
} }
val builder = AlertDialog.Builder(cxt, R.style.Theme_AppCompat) val builder = AlertDialog.Builder(cxt, R.style.Theme_AppCompat)
builder.setMessage("Do you want to call or send message to '" + builder.setMessage("Do you want to call or send message to '" +
"${ContactsActivity.contacts[pos].name}'?") "${Contact.contacts()[pos].name}'?")
.setNeutralButton("Call", dialogClickListener) .setNeutralButton("Call", dialogClickListener)
.setNegativeButton("Send Message", dialogClickListener) .setNegativeButton("Send Message", dialogClickListener)
.setPositiveButton("Cancel", dialogClickListener) .setPositiveButton("Cancel", dialogClickListener)
@@ -66,7 +66,7 @@ class ContactListAdapter(private val cxt: Context, private val rows: ArrayList<C
val dialogClickListener = DialogInterface.OnClickListener { _, which -> val dialogClickListener = DialogInterface.OnClickListener { _, which ->
when (which) { when (which) {
DialogInterface.BUTTON_NEGATIVE -> { DialogInterface.BUTTON_NEGATIVE -> {
ContactsActivity.contacts.removeAt(pos) Contact.contacts().removeAt(pos)
ContactsActivity.saveContacts() ContactsActivity.saveContacts()
this.notifyDataSetChanged() this.notifyDataSetChanged()
} }
@@ -76,7 +76,7 @@ class ContactListAdapter(private val cxt: Context, private val rows: ArrayList<C
} }
val builder = AlertDialog.Builder(cxt, R.style.Theme_AppCompat) val builder = AlertDialog.Builder(cxt, R.style.Theme_AppCompat)
builder.setMessage("Do you want to delete contact '" + builder.setMessage("Do you want to delete contact '" +
"${ContactsActivity.contacts[pos].name}'?") "${Contact.contacts()[pos].name}'?")
.setPositiveButton("Cancel", dialogClickListener) .setPositiveButton("Cancel", dialogClickListener)
.setNegativeButton("Delete Contact", dialogClickListener) .setNegativeButton("Delete Contact", dialogClickListener)
.show() .show()
@@ -10,7 +10,6 @@ import android.widget.ImageButton
import android.widget.ListView import android.widget.ListView
import java.io.File import java.io.File
import java.util.ArrayList
class ContactsActivity : AppCompatActivity() { class ContactsActivity : AppCompatActivity() {
@@ -20,16 +19,22 @@ class ContactsActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contacts) setContentView(R.layout.activity_contacts)
filesPath = applicationContext.filesDir.absolutePath
val listView = findViewById(R.id.contacts) as ListView val listView = findViewById(R.id.contacts) as ListView
Log.d("Baresip", "Got ${contacts.size} contacts")
val aor = intent.extras.getString("aor") val aor = intent.extras.getString("aor")
clAdapter = ContactListAdapter(this, contacts, aor) clAdapter = ContactListAdapter(this, Contact.contacts(), aor)
listView.adapter = clAdapter listView.adapter = clAdapter
listView.isLongClickable = true listView.isLongClickable = true
val plusButton = findViewById(R.id.plusButton) as ImageButton val plusButton = findViewById(R.id.plusButton) as ImageButton
plusButton.setOnClickListener { plusButton.setOnClickListener {
if (Contact.contacts().size >= Contact.CONTACTS_SIZE) {
Utils.alertView(this, "Notice",
"Your maximum number of contacts " +
"(${Contact.CONTACTS_SIZE}) has been exceeded.")
} else {
val i = Intent(this, ContactActivity::class.java) val i = Intent(this, ContactActivity::class.java)
val b = Bundle() val b = Bundle()
b.putBoolean("new", true) b.putBoolean("new", true)
@@ -38,6 +43,7 @@ class ContactsActivity : AppCompatActivity() {
startActivityForResult(i, MainActivity.CONTACT_CODE) startActivityForResult(i, MainActivity.CONTACT_CODE)
} }
} }
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == RESULT_OK) clAdapter.notifyDataSetChanged() if (resultCode == RESULT_OK) clAdapter.notifyDataSetChanged()
@@ -64,34 +70,32 @@ class ContactsActivity : AppCompatActivity() {
companion object { companion object {
var contacts = ArrayList<Contact>() internal var filesPath = ""
fun saveContacts() { fun saveContacts(path: String = filesPath) {
var contents = "" var contents = ""
for (c in contacts) for (c in Contact.contacts())
contents += "\"${c.name}\" ${c.uri}\n" contents += "\"${c.name}\" ${c.uri}\n"
val path = MainActivity.filesPath + "/contacts" Utils.putFileContents(File(path + "/contacts"), contents)
Utils.putFileContents(File(path), contents)
Log.d("Baresip", "Saved contacts '${contents}' to '$path")
} }
fun restoreContacts(path: String) { fun restoreContacts(path: String = filesPath) {
val content = Utils.getFileContents(File(path + "/contacts")) val content = Utils.getFileContents(File(path + "/contacts"))
Api.contacts_remove() Api.contacts_remove()
contacts.clear() Contact.contacts().clear()
content.lines().forEach { content.lines().forEach {
val parts = it.split("\"") val parts = it.split("\"")
if (parts.size == 3) { if (parts.size == 3) {
val name = parts[1] val name = parts[1]
val uri = parts[2].trim() val uri = parts[2].trim()
Api.contact_add("\"$name\" $uri") Api.contact_add("\"$name\" $uri")
contacts.add(Contact(name, uri)) Contact.contacts().add(Contact(name, uri))
} }
} }
} }
fun findContactURI(name: String): String { fun findContactURI(name: String): String {
for (c in contacts) for (c in Contact.contacts())
if (c.name == name) if (c.name == name)
return c.uri.removePrefix("<") return c.uri.removePrefix("<")
.replaceAfter(">", "") .replaceAfter(">", "")
@@ -100,13 +104,13 @@ class ContactsActivity : AppCompatActivity() {
} }
fun nameExists(name: String): Boolean { fun nameExists(name: String): Boolean {
for (c in contacts) for (c in Contact.contacts())
if (c.name.equals(name, ignoreCase = true)) return true if (c.name.equals(name, ignoreCase = true)) return true
return false return false
} }
fun contactName(uri: String): String { fun contactName(uri: String): String {
for (c in contacts) for (c in Contact.contacts())
if ((Utils.uriUserPart(c.uri) == Utils.uriUserPart(uri)) && if ((Utils.uriUserPart(c.uri) == Utils.uriUserPart(uri)) &&
(Utils.uriHostPart(c.uri) == Utils.uriHostPart(uri))) (Utils.uriHostPart(c.uri) == Utils.uriHostPart(uri)))
return c.name return c.name
@@ -7,10 +7,10 @@ import android.support.v7.app.AppCompatActivity
import android.util.Log import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.Menu import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.widget.EditText import android.widget.EditText
import android.widget.TextView import android.widget.TextView
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
@@ -27,8 +27,7 @@ class EditConfigActivity : AppCompatActivity() {
setContentView(R.layout.activity_edit_config) setContentView(R.layout.activity_edit_config)
editText = findViewById(R.id.editConfig) as EditText editText = findViewById(R.id.editConfig) as EditText
path = applicationContext.filesDir.absolutePath + "/config" path = "${applicationContext.filesDir.absolutePath}/config"
// path = "/sdcard/baresip/config";
file = File(path!!) file = File(path!!)
var content: String var content: String
@@ -1,7 +1,6 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.Manifest import android.Manifest
import android.annotation.TargetApi
import android.app.NotificationManager import android.app.NotificationManager
import android.content.* import android.content.*
import android.support.v4.app.ActivityCompat import android.support.v4.app.ActivityCompat
@@ -15,22 +14,18 @@ import android.view.MenuItem
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Color import android.graphics.Color
import android.media.* import android.media.*
import android.os.Build
import android.support.v4.content.LocalBroadcastManager import android.support.v4.content.LocalBroadcastManager
import android.support.v7.view.menu.ActionMenuItemView import android.support.v7.view.menu.ActionMenuItemView
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.text.Editable
import android.text.InputType import android.text.InputType
import android.text.TextWatcher import android.text.TextWatcher
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.* import android.widget.*
import android.view.* import android.view.*
import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
lateinit var appContext: Context
internal lateinit var layout: RelativeLayout internal lateinit var layout: RelativeLayout
internal lateinit var baresipService: Intent internal lateinit var baresipService: Intent
internal lateinit var callTitle: TextView internal lateinit var callTitle: TextView
@@ -51,7 +46,6 @@ class MainActivity : AppCompatActivity() {
internal lateinit var aorSpinner: Spinner internal lateinit var aorSpinner: Spinner
internal lateinit var am: AudioManager internal lateinit var am: AudioManager
internal lateinit var rt: Ringtone internal lateinit var rt: Ringtone
internal var rtTimer: Timer? = null
internal lateinit var imm: InputMethodManager internal lateinit var imm: InputMethodManager
internal lateinit var nm: NotificationManager internal lateinit var nm: NotificationManager
internal lateinit var serviceEventReceiver: BroadcastReceiver internal lateinit var serviceEventReceiver: BroadcastReceiver
@@ -69,9 +63,15 @@ class MainActivity : AppCompatActivity() {
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
filesPath = applicationContext.filesDir.absolutePath if (ContextCompat.checkSelfPermission(applicationContext,
Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_DENIED) {
Toast.makeText(applicationContext,"You have not granted microphone permission.",
Toast.LENGTH_SHORT).show()
finishAndRemoveTask()
System.exit(0)
return
}
appContext = applicationContext
layout = findViewById(R.id.mainActivityLayout) as RelativeLayout layout = findViewById(R.id.mainActivityLayout) as RelativeLayout
callTitle = findViewById(R.id.callTitle) as TextView callTitle = findViewById(R.id.callTitle) as TextView
callUri = findViewById(R.id.callUri) as AutoCompleteTextView callUri = findViewById(R.id.callUri) as AutoCompleteTextView
@@ -112,16 +112,16 @@ class MainActivity : AppCompatActivity() {
} }
aorSpinner = findViewById(R.id.AoRList) aorSpinner = findViewById(R.id.AoRList)
uaAdapter = UaSpinnerAdapter(applicationContext, uas, images) uaAdapter = UaSpinnerAdapter(applicationContext, UserAgent.uas(), UserAgent.status())
aorSpinner.adapter = uaAdapter 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) {
Log.d("Baresip", "aorSpinner selecting $position") Log.d("Baresip", "aorSpinner selecting $position")
val acc = uas[position].account val acc = UserAgent.uas()[position].account
val aor = acc.aor val aor = acc.aor
val ua = uas[position] val ua = UserAgent.uas()[position]
Log.d("Baresip", "Setting $aor current") Log.d("Baresip", "Setting $aor current")
uag_current_set(uas[position].uap) Api.uag_current_set(UserAgent.uas()[position].uap)
showCall(ua) showCall(ua)
if (acc.vmUri != "") { if (acc.vmUri != "") {
if (acc.vmNew > 0) if (acc.vmNew > 0)
@@ -151,11 +151,12 @@ class MainActivity : AppCompatActivity() {
true true
} else { } else {
if ((event.x - view.left) < 100) { if ((event.x - view.left) < 100) {
val i = Intent(this, AccountActivity::class.java) val i = Intent(this, AccountsActivity::class.java)
val b = Bundle() val b = Bundle()
b.putString("accp", uas[aorSpinner.selectedItemPosition].account.accp) b.putString("accp",
UserAgent.uas()[aorSpinner.selectedItemPosition].account.accp)
i.putExtras(b) i.putExtras(b)
startActivityForResult(i, ACCOUNT_CODE) startActivityForResult(i, ACCOUNTS_CODE)
true true
} else { } else {
false false
@@ -165,11 +166,12 @@ class MainActivity : AppCompatActivity() {
false false
} }
} }
callUri.threshold = 2
ContactsActivity.restoreContacts(applicationContext.filesDir.path)
callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
ContactsActivity.contacts.map{Contact -> Contact.name})) Contact.contacts().map{Contact -> Contact.name}))
callUri.threshold = 2
ContactsActivity.restoreContacts(applicationContext.filesDir.absolutePath)
callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
Contact.contacts().map{Contact -> Contact.name}))
securityButton.setOnClickListener { securityButton.setOnClickListener {
when (securityButton.tag) { when (securityButton.tag) {
@@ -186,7 +188,8 @@ 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 calls = Call.uaCalls(calls, uas[aorSpinner.selectedItemPosition], "") val calls = Call.uaCalls(UserAgent.uas()[aorSpinner.selectedItemPosition],
"")
if (calls.size > 0) { if (calls.size > 0) {
if (Api.cmd_exec("zrtp_unverify " + calls[0].zid) != 0) { if (Api.cmd_exec("zrtp_unverify " + calls[0].zid) != 0) {
Log.w("Baresip", Log.w("Baresip",
@@ -207,9 +210,10 @@ class MainActivity : AppCompatActivity() {
} }
callButton.setOnClickListener { callButton.setOnClickListener {
val ua = uas[aorSpinner.selectedItemPosition] callUri.setAdapter(null)
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor val aor = ua.account.aor
if (!ONE_CALL_ONLY || calls.isEmpty()) { if (Call.calls().isEmpty()) {
val uriText = callUri.text.toString().trim() val uriText = callUri.text.toString().trim()
if (uriText.length > 0) { if (uriText.length > 0) {
var uri = ContactsActivity.findContactURI(uriText) var uri = ContactsActivity.findContactURI(uriText)
@@ -225,66 +229,66 @@ class MainActivity : AppCompatActivity() {
else else
call(ua, uri) call(ua, uri)
} else { } else {
val latest = CallHistory.aorLatestHistory(history, aor) val latest = CallHistory.aorLatestHistory(aor)
if (latest != null) if (latest != null)
if (Utils.uriHostPart(latest.peerURI) == Utils.uriHostPart(aor)) callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(latest.peerURI),
callUri.setText(Utils.uriUserPart(latest.peerURI)) Utils.aorDomain(ua.account.aor)))
else
callUri.setText(latest.peerURI)
} }
} }
} }
hangupButton.setOnClickListener { hangupButton.setOnClickListener {
val ua = uas[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor val aor = ua.account.aor
val uaCalls = Call.uaCalls(calls, ua, "") val uaCalls = Call.uaCalls(ua, "")
if (uaCalls.size > 0) { if (uaCalls.size > 0) {
val callp = uaCalls[uaCalls.size - 1].callp val callp = uaCalls[uaCalls.size - 1].callp
Log.d("Baresip", "AoR $aor hanging up call $callp with ${callUri.text}") Log.d("Baresip", "AoR $aor hanging up call $callp with ${callUri.text}")
hangupButton.isEnabled = false hangupButton.isEnabled = false
ua_hangup(ua.uap, callp, 0, "") Api.ua_hangup(ua.uap, callp, 0, "")
} }
} }
answerButton.setOnClickListener { answerButton.setOnClickListener {
val ua = uas[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor val aor = ua.account.aor
val callp = Call.uaCalls(calls, ua,"in")[0].callp val callp = Call.uaCalls(ua,"in")[0].callp
Log.d("Baresip", "AoR $aor answering call $callp from ${callUri.text}") Log.d("Baresip", "AoR $aor answering call $callp from ${callUri.text}")
answerButton.isEnabled = false answerButton.isEnabled = false
ua_answer(ua.uap, callp) Api.ua_answer(ua.uap, callp)
} }
rejectButton.setOnClickListener { rejectButton.setOnClickListener {
val ua = uas[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor val aor = ua.account.aor
val callp = Call.uaCalls(calls, ua,"in")[0].callp val callp = Call.uaCalls(ua,"in")[0].callp
Log.d("Baresip", "AoR $aor rejecting call $callp from ${callUri.text}") Log.d("Baresip", "AoR $aor rejecting call $callp from ${callUri.text}")
rejectButton.isEnabled = false rejectButton.isEnabled = false
ua_hangup(ua.uap, callp, 486, "Rejected") Api.ua_hangup(ua.uap, callp, 486, "Rejected")
} }
holdButton.setOnClickListener { holdButton.setOnClickListener {
val ua = uas[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor val aor = ua.account.aor
val call = Call.uaCalls(calls, ua,"")[0] val call = Call.uaCalls(ua,"")[0]
if (call.onhold) { if (call.onhold) {
Log.d("Baresip", "AoR $aor resuming call ${call.callp} with ${callUri.text}") Log.d("Baresip", "AoR $aor resuming call ${call.callp} with ${callUri.text}")
call_unhold(call.callp) Api.call_unhold(call.callp)
call.onhold = false call.onhold = false
holdButton.setImageResource(R.drawable.pause) holdButton.setImageResource(R.drawable.pause)
} else { } else {
Log.d("Baresip", "AoR $aor holding call ${call.callp} with ${callUri.text}") Log.d("Baresip", "AoR $aor holding call ${call.callp} with ${callUri.text}")
call_hold(call.callp) Api.call_hold(call.callp)
call.onhold = true call.onhold = true
holdButton.setImageResource(R.drawable.play) holdButton.setImageResource(R.drawable.play)
} }
} }
dtmf.tag = ArrayList<TextWatcher>()
voicemailButton.setOnClickListener { voicemailButton.setOnClickListener {
if (aorSpinner.selectedItemPosition >= 0) { if (aorSpinner.selectedItemPosition >= 0) {
val ua = uas[aorSpinner.selectedItemPosition] val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val acc = ua.account val acc = ua.account
if (acc.vmUri != "") { if (acc.vmUri != "") {
val dialogClickListener = DialogInterface.OnClickListener { _, which -> val dialogClickListener = DialogInterface.OnClickListener { _, which ->
@@ -317,19 +321,19 @@ class MainActivity : AppCompatActivity() {
val i = Intent(this@MainActivity, ContactsActivity::class.java) val i = Intent(this@MainActivity, ContactsActivity::class.java)
val b = Bundle() val b = Bundle()
if (aorSpinner.selectedItemPosition >= 0) if (aorSpinner.selectedItemPosition >= 0)
b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor) b.putString("aor", UserAgent.uas()[aorSpinner.selectedItemPosition].account.aor)
else else
b.putString("aor", "") b.putString("aor", "")
i.putExtras(b) i.putExtras(b)
startActivityForResult(i, CONTACTS_CODE) startActivityForResult(i, CONTACTS_CODE)
} }
ChatsActivity.restoreMessages(applicationContext.filesDir.path) ChatsActivity.restoreMessages(applicationContext.filesDir.absolutePath)
messagesButton.setOnClickListener { messagesButton.setOnClickListener {
if (aorSpinner.selectedItemPosition >= 0) { if (aorSpinner.selectedItemPosition >= 0) {
val i = Intent(this@MainActivity, ChatsActivity::class.java) val i = Intent(this@MainActivity, ChatsActivity::class.java)
val b = Bundle() val b = Bundle()
b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor) b.putString("aor", UserAgent.uas()[aorSpinner.selectedItemPosition].account.aor)
b.putString("peer", "") b.putString("peer", "")
b.putBoolean("focus", false) b.putBoolean("focus", false)
i.putExtras(b) i.putExtras(b)
@@ -337,12 +341,12 @@ class MainActivity : AppCompatActivity() {
} }
} }
CallsActivity.restoreHistory(applicationContext.filesDir.path) CallHistory.restore(applicationContext.filesDir.absolutePath)
callsButton.setOnClickListener { callsButton.setOnClickListener {
if (aorSpinner.selectedItemPosition >= 0) { if (aorSpinner.selectedItemPosition >= 0) {
val i = Intent(this@MainActivity, CallsActivity::class.java) val i = Intent(this@MainActivity, CallsActivity::class.java)
val b = Bundle() val b = Bundle()
b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor) b.putString("aor", UserAgent.uas()[aorSpinner.selectedItemPosition].account.aor)
i.putExtras(b) i.putExtras(b)
startActivityForResult(i, CALLS_CODE) startActivityForResult(i, CALLS_CODE)
} }
@@ -363,7 +367,7 @@ class MainActivity : AppCompatActivity() {
} }
baresipService = Intent(this@MainActivity, BaresipService::class.java) baresipService = Intent(this@MainActivity, BaresipService::class.java)
if (!BaresipService.IS_SERVICE_RUNNING) { if (!BaresipService.isServiceRunning) {
baresipService.setAction("Start") baresipService.setAction("Start")
startService(baresipService) startService(baresipService)
} }
@@ -372,7 +376,7 @@ class MainActivity : AppCompatActivity() {
moveTaskToBack(true) moveTaskToBack(true)
if (intent.hasExtra("action")) { if (intent.hasExtra("action")) {
// MainActivity was not running when call or message came in // MainActivity was not visible when call, message, or transfer request came in
handleIntent(intent) handleIntent(intent)
} }
} }
@@ -385,7 +389,7 @@ class MainActivity : AppCompatActivity() {
return return
} }
val uap = params[0] val uap = params[0]
val ua = UserAgent.find(uas, uap) val ua = UserAgent.find(uap)
if (ua == null) { if (ua == null) {
Log.w("Baresip", "handleServiceEvent '$event' did not find ua $uap") Log.w("Baresip", "handleServiceEvent '$event' did not find ua $uap")
return return
@@ -394,8 +398,8 @@ class MainActivity : AppCompatActivity() {
Log.d("Baresip", "Handling service event '${ev[0]}' for $uap") Log.d("Baresip", "Handling service event '${ev[0]}' for $uap")
val aor = ua.account.aor val aor = ua.account.aor
val acc = ua.account val acc = ua.account
for (account_index in uas.indices) { for (account_index in UserAgent.uas().indices) {
if (uas[account_index].account.aor == aor) { if (UserAgent.uas()[account_index].account.aor == aor) {
when (ev[0]) { when (ev[0]) {
"ua added" -> { "ua added" -> {
uaAdapter.notifyDataSetChanged() uaAdapter.notifyDataSetChanged()
@@ -406,39 +410,24 @@ class MainActivity : AppCompatActivity() {
} }
"call ringing" -> { "call ringing" -> {
} }
"call incoming" -> { "call rejected" -> {
val callp = params[1] if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
if (ContextCompat.checkSelfPermission(applicationContext,
Manifest.permission.RECORD_AUDIO) ==
PackageManager.PERMISSION_DENIED) {
Toast.makeText(applicationContext,
"You have not granted microphone permission.",
Toast.LENGTH_SHORT).show()
ua_hangup(uap, callp, 486, "Busy Here")
return
}
val peer_uri = Api.call_peeruri(callp)
val new_call = Call(callp, ua, peer_uri, "in", "incoming",
dtmfWatcher(callp))
if (ONE_CALL_ONLY && (calls.size > 0)) {
Log.d("Baresip", "Auto-rejecting incoming call $uap/$callp/$peer_uri")
ua_hangup(uap, callp, 486, "Busy Here")
if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE)
CallHistory.aorRemoveHistory(history, aor)
history.add(CallHistory(aor, peer_uri, "in", false))
CallsActivity.saveHistory()
acc.missedCalls = true
if (ua == uas[aorSpinner.selectedItemPosition]) {
callsButton.setImageResource(R.drawable.calls_missed) callsButton.setImageResource(R.drawable.calls_missed)
} }
} else { }
Log.d("Baresip", "Incoming call $uap/$callp/$peer_uri") "call incoming" -> {
calls.add(new_call) val callp = params[1]
if (ua != uas[aorSpinner.selectedItemPosition]) { val call = Call.find(callp)
if (call == null) {
Log.e("Baresip", "Incoming call $callp not found")
return
}
if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition]) {
aorSpinner.setSelection(account_index) aorSpinner.setSelection(account_index)
} else { } else {
callTitle.text = "Incoming call from ..." callTitle.text = "Incoming call from ..."
callUri.setText(new_call.peerURI) callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(call.peerURI),
Utils.aorDomain(ua.account.aor)))
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
callButton.visibility = View.INVISIBLE callButton.visibility = View.INVISIBLE
hangupButton.visibility = View.INVISIBLE hangupButton.visibility = View.INVISIBLE
@@ -449,7 +438,6 @@ class MainActivity : AppCompatActivity() {
holdButton.visibility = View.INVISIBLE holdButton.visibility = View.INVISIBLE
dtmf.visibility = View.INVISIBLE dtmf.visibility = View.INVISIBLE
} }
startRinging()
if (Utils.isVisible()) { if (Utils.isVisible()) {
Log.d("Baresip", "Baresip is visible") Log.d("Baresip", "Baresip is visible")
val i = Intent(applicationContext, MainActivity::class.java) val i = Intent(applicationContext, MainActivity::class.java)
@@ -457,19 +445,14 @@ class MainActivity : AppCompatActivity() {
startActivity(i) startActivity(i)
} }
} }
}
"call established" -> { "call established" -> {
val callp = params[1] val callp = params[1]
val call = Call.find(calls, callp) val call = Call.find(callp)
if (call == null) { if (call == null) {
Log.e("Baresip", "Established call $callp not found") Log.e("Baresip", "Established call $callp not found")
return return
} }
Log.d("Baresip", "AoR $aor call $callp established") if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
call.status = "connected"
call.onhold = false
call.security = R.drawable.box_red
if (ua == uas[aorSpinner.selectedItemPosition]) {
if (call.dir == "in") if (call.dir == "in")
callTitle.text = "Incoming call from ..." callTitle.text = "Incoming call from ..."
else else
@@ -492,28 +475,13 @@ class MainActivity : AppCompatActivity() {
dtmf.hint = "DTMF" dtmf.hint = "DTMF"
dtmf.visibility = View.VISIBLE dtmf.visibility = View.VISIBLE
dtmf.requestFocus() dtmf.requestFocus()
}
(dtmf.tag as ArrayList<TextWatcher>).add(call.dtmfWatcher!!)
dtmf.addTextChangedListener(call.dtmfWatcher) dtmf.addTextChangedListener(call.dtmfWatcher)
} }
if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE)
CallHistory.aorRemoveHistory(history, aor)
history.add(CallHistory(aor, call.peerURI, call.dir, true))
CallsActivity.saveHistory()
call.hasHistory = true
if (rtTimer != null) {
rtTimer!!.cancel()
rtTimer = null
if (rt.isPlaying) rt.stop()
}
am.mode = AudioManager.MODE_IN_COMMUNICATION
requestAudioFocus( AudioManager.STREAM_VOICE_CALL)
am.isSpeakerphoneOn = false
// am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
// (am.getStreamMaxVolume(STREAM_VOICE_CALL) / 2) + 1,
// AudioManager.STREAM_VOICE_CALL)
}
"call verify" -> { "call verify" -> {
val callp = params[1] val callp = params[1]
val call = Call.find(calls, callp) val call = Call.find(callp)
if (call == null) { if (call == null) {
Log.e("Baresip", "Call $callp to be verified is not found") Log.e("Baresip", "Call $callp to be verified is not found")
return return
@@ -531,7 +499,7 @@ class MainActivity : AppCompatActivity() {
} }
call.security = security call.security = security
call.zid = ev[3] call.zid = ev[3]
if (ua == uas[aorSpinner.selectedItemPosition]) { if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
securityButton.setImageResource(security) securityButton.setImageResource(security)
setSecurityButtonTag(securityButton, security) setSecurityButtonTag(securityButton, security)
securityButton.visibility = View.VISIBLE securityButton.visibility = View.VISIBLE
@@ -541,7 +509,7 @@ class MainActivity : AppCompatActivity() {
verifyDialog.setNegativeButton("No") { dialog, _ -> verifyDialog.setNegativeButton("No") { dialog, _ ->
call.security = R.drawable.box_yellow call.security = R.drawable.box_yellow
call.zid = ev[3] call.zid = ev[3]
if (ua == uas[aorSpinner.selectedItemPosition]) { if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
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
@@ -552,7 +520,7 @@ class MainActivity : AppCompatActivity() {
} }
"call verified", "call secure" -> { "call verified", "call secure" -> {
val callp = params[1] val callp = params[1]
val call = Call.find(calls, callp) val call = Call.find(callp)
if (call == null) { if (call == null) {
Log.e("Baresip", "Call $callp that is verified is not found") Log.e("Baresip", "Call $callp that is verified is not found")
return return
@@ -566,7 +534,7 @@ class MainActivity : AppCompatActivity() {
tag = "green" tag = "green"
call.zid = ev[1] call.zid = ev[1]
} }
if (ua == uas[aorSpinner.selectedItemPosition]) { if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
securityButton.setImageResource(call.security) securityButton.setImageResource(call.security)
securityButton.tag = tag securityButton.tag = tag
securityButton.visibility = View.VISIBLE securityButton.visibility = View.VISIBLE
@@ -574,86 +542,53 @@ class MainActivity : AppCompatActivity() {
} }
"call transfer" -> { "call transfer" -> {
val callp = params[1] val callp = params[1]
val call = Call.find(calls, callp) val call = Call.find(callp)
if (call == null) { if (call == null) {
Log.e("Baresip", "Call $callp to be transfered is not found") Log.e("Baresip", "Call $callp to be transferred is not found")
return return
} }
val transferDialog = AlertDialog.Builder(this) val transferDialog = AlertDialog.Builder(this)
transferDialog.setMessage("Do you want to transfer this call to ${ev[1]}?") val target = Utils.friendlyUri(ContactsActivity.contactName(ev[1]),
Utils.aorDomain(aor))
transferDialog.setMessage("Do you accept to transfer call to $target?")
transferDialog.setPositiveButton("Yes") { dialog, _ -> transferDialog.setPositiveButton("Yes") { dialog, _ ->
if (call in calls) if (call in Call.calls())
transfer(ua, call, ev[1]) transfer(ua, call, ev[1])
else else
call(ua, ev[1]) call(ua, ev[1])
dialog.dismiss() dialog.dismiss()
} }
transferDialog.setNegativeButton("No") { dialog, _ -> transferDialog.setNegativeButton("No") { dialog, _ ->
if (call in calls) if (call in Call.calls())
call_notify_sipfrag(callp, 603, "Decline") Api.call_notify_sipfrag(callp, 603, "Decline")
dialog.dismiss() dialog.dismiss()
} }
transferDialog.create().show() transferDialog.create().show()
} }
"transfer failed" -> {
val callp = params[1]
Log.d("Baresip", "AoR $aor hanging up call $callp with ${ev[1]}")
ua_hangup(ua.uap, callp, 0, "")
}
"call closed" -> { "call closed" -> {
val callp = params[1] val watchers = dtmf.tag as ArrayList<TextWatcher>
val call = Call.find(calls, callp) if (watchers.size > 0) {
if (call == null) { dtmf.removeTextChangedListener(watchers[0])
Log.d("Baresip", "Call $callp that is closed is not found") watchers.removeAt(0)
return
} }
Log.d("Baresip", "Removing call ${uap}/${callp}/" + call.peerURI) if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
stopRinging()
if (ua == uas[aorSpinner.selectedItemPosition]) {
dtmf.removeTextChangedListener(call.dtmfWatcher)
}
calls.remove(call)
if (ua == uas[aorSpinner.selectedItemPosition]) {
showCall(ua) showCall(ua)
} if (ua.account.missedCalls)
if (!call.hasHistory) {
if (CallHistory.aorHistory(history, aor) > HISTORY_SIZE)
CallHistory.aorRemoveHistory(history, aor)
history.add(CallHistory(aor, call.peerURI, call.dir,false))
CallsActivity.saveHistory()
if (call.dir == "in") {
acc.missedCalls = true
if (ua == uas[aorSpinner.selectedItemPosition])
callsButton.setImageResource(R.drawable.calls_missed) callsButton.setImageResource(R.drawable.calls_missed)
} }
}
if (calls.size == 0) {
am.mode = AudioManager.MODE_NORMAL
} else {
val uaCalls = Call.uaCalls(calls, ua, "")
if (uaCalls.size > 0) call_start_audio(uaCalls[uaCalls.size - 1].callp)
}
if (am.isSpeakerphoneOn) {
am.isSpeakerphoneOn = false
val speakerIcon = findViewById(R.id.speakerIcon) as ActionMenuItemView val speakerIcon = findViewById(R.id.speakerIcon) as ActionMenuItemView
speakerIcon.setBackgroundColor(ContextCompat.getColor(applicationContext, speakerIcon.setBackgroundColor(ContextCompat.getColor(applicationContext,
R.color.colorPrimary)) R.color.colorPrimary))
} }
if (audioFocused) {
abandonAudioFocus()
}
}
"message" -> { "message" -> {
val peerUri = params[1] val peerUri = params[1]
val msgText = params[2] val msgText = params[2]
val time = params[3] val time = params[3]
val newMsg = Message(aor, peerUri, R.drawable.arrow_down_green, msgText,
time.toLong(), true)
Log.d("Baresip", "Incoming message $aor/$peerUri/$msgText") Log.d("Baresip", "Incoming message $aor/$peerUri/$msgText")
messages.add(newMsg) Message.add(Message(aor, peerUri, R.drawable.arrow_down_green, msgText,
ChatsActivity.saveMessages() time.toLong(), true))
if (Utils.isVisible()) { if (Utils.isVisible()) {
if (ua != uas[aorSpinner.selectedItemPosition]) if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition])
aorSpinner.setSelection(account_index) aorSpinner.setSelection(account_index)
val i = Intent(applicationContext, ChatsActivity::class.java) val i = Intent(applicationContext, ChatsActivity::class.java)
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
@@ -676,7 +611,7 @@ class MainActivity : AppCompatActivity() {
} }
} }
if ((aorSpinner.selectedItemPosition >= 0) && if ((aorSpinner.selectedItemPosition >= 0) &&
(ua == uas[aorSpinner.selectedItemPosition])) (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]))
if (acc.vmNew > 0) if (acc.vmNew > 0)
voicemailButton.setImageResource(R.drawable.voicemail_new) voicemailButton.setImageResource(R.drawable.voicemail_new)
else else
@@ -702,26 +637,19 @@ class MainActivity : AppCompatActivity() {
Log.d("Baresip", "Handling intent '$action'") Log.d("Baresip", "Handling intent '$action'")
when (action) { when (action) {
"call" -> { "call" -> {
if (!calls.isEmpty()) { if (!Call.calls().isEmpty()) {
Toast.makeText(applicationContext, "You already have an active call!", Toast.makeText(applicationContext, "You already have an active call!",
Toast.LENGTH_SHORT).show() Toast.LENGTH_SHORT).show()
return return
} }
val uap = intent.getStringExtra("uap") val uap = intent.getStringExtra("uap")
val ua = UserAgent.find(MainActivity.uas, uap) val ua = UserAgent.find(uap)
if (ua == null) { if (ua == null) {
Log.e("Baresip", "onNewIntent did not find ua $uap") Log.e("Baresip", "handleIntent 'call' did not find ua $uap")
return return
} }
val aor = ua.account.aor if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition])
if (ua != uas[aorSpinner.selectedItemPosition]) { spinToAor(ua.account.aor)
for (account_index in uas.indices) {
if (uas[account_index].account.aor == aor) {
aorSpinner.setSelection(account_index)
break
}
}
}
makeCall = intent.getStringExtra("peer") makeCall = intent.getStringExtra("peer")
} }
"answer" -> { "answer" -> {
@@ -731,9 +659,33 @@ class MainActivity : AppCompatActivity() {
rejectCall = intent.getStringExtra("callp") rejectCall = intent.getStringExtra("callp")
moveTaskToBack(true) moveTaskToBack(true)
} }
"transfer" -> {
val callp = intent.getStringExtra("callp")
val call = Call.find(callp)
val uap = intent.getStringExtra("uap")
val ua = UserAgent.find(uap)
if (ua == null) {
Log.e("Baresip", "Ua $uap of call transfer target does not exist")
moveTaskToBack(true)
return
}
if (call == null)
call(ua, intent.getStringExtra("uri"))
else
transfer(ua, call, intent.getStringExtra("uri"))
}
"deny" -> {
val callp = intent.getStringExtra("callp")
val call = Call.find(callp)
if (call == null)
Log.d("Baresip", "Call $callp denied transfer does not exist anymore")
else
Api.call_notify_sipfrag(callp, 603, "Decline")
moveTaskToBack(true)
}
"reply", "save", "delete" -> { "reply", "save", "delete" -> {
val uap = intent.getStringExtra("uap") val uap = intent.getStringExtra("uap")
val ua = UserAgent.find(MainActivity.uas, uap) val ua = UserAgent.find(uap)
if (ua == null) { if (ua == null) {
Log.e("Baresip", "onNewIntent did not find ua $uap") Log.e("Baresip", "onNewIntent did not find ua $uap")
return return
@@ -743,14 +695,8 @@ class MainActivity : AppCompatActivity() {
"reply" -> { "reply" -> {
val i = Intent(this@MainActivity, ChatsActivity::class.java) val i = Intent(this@MainActivity, ChatsActivity::class.java)
val b = Bundle() val b = Bundle()
if (ua != uas[aorSpinner.selectedItemPosition]) { if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition])
for (account_index in uas.indices) { spinToAor(aor)
if (uas[account_index].account.aor == aor) {
aorSpinner.setSelection(account_index)
break
}
}
}
b.putString("aor", aor) b.putString("aor", aor)
b.putString("peer", intent.getStringExtra("peer")) b.putString("peer", intent.getStringExtra("peer"))
b.putBoolean("focus", true) b.putBoolean("focus", true)
@@ -759,19 +705,21 @@ class MainActivity : AppCompatActivity() {
} }
"save" -> { "save" -> {
ChatsActivity.saveUaMessage(ua.account.aor, ChatsActivity.saveUaMessage(ua.account.aor,
intent.getStringExtra("time").toLong()) intent.getStringExtra("time").toLong(),
applicationContext.filesDir.absolutePath)
moveTaskToBack(true) moveTaskToBack(true)
} }
"delete" -> { "delete" -> {
ChatsActivity.deleteUaMessage(ua.account.aor, ChatsActivity.deleteUaMessage(ua.account.aor,
intent.getStringExtra("time").toLong()) intent.getStringExtra("time").toLong(),
applicationContext.filesDir.absolutePath)
moveTaskToBack(true) moveTaskToBack(true)
} }
} }
nm.cancel(BaresipService.MESSAGE_NOTIFICATION_ID) nm.cancel(BaresipService.MESSAGE_NOTIFICATION_ID)
} }
"message" -> { "message" -> {
val ua = UserAgent.find(MainActivity.uas, intent.getStringExtra("uap"))!! val ua = UserAgent.find(intent.getStringExtra("uap"))!!
val i = Intent(applicationContext, ChatsActivity::class.java) val i = Intent(applicationContext, ChatsActivity::class.java)
val b = Bundle() val b = Bundle()
b.putString("aor", ua.account.aor) b.putString("aor", ua.account.aor)
@@ -816,7 +764,7 @@ class MainActivity : AppCompatActivity() {
override fun onDestroy() { override fun onDestroy() {
Log.d("Baresip", "Destroyed") Log.d("Baresip", "Destroyed")
LocalBroadcastManager.getInstance(this).unregisterReceiver(serviceEventReceiver) LocalBroadcastManager.getInstance(this).unregisterReceiver(serviceEventReceiver)
if (BaresipService.IS_SERVICE_RUNNING) { if (BaresipService.isServiceRunning) {
baresipService.setAction("Kill") baresipService.setAction("Kill")
startService(baresipService) startService(baresipService)
} }
@@ -844,6 +792,9 @@ class MainActivity : AppCompatActivity() {
} }
R.id.accounts -> { R.id.accounts -> {
i = Intent(this, AccountsActivity::class.java) i = Intent(this, AccountsActivity::class.java)
val b = Bundle()
b.putString("accp", "")
i.putExtras(b)
startActivityForResult(i, ACCOUNTS_CODE) startActivityForResult(i, ACCOUNTS_CODE)
return true return true
} }
@@ -860,7 +811,7 @@ class MainActivity : AppCompatActivity() {
} }
R.id.quit -> { R.id.quit -> {
Log.d("Baresip", "Quiting") Log.d("Baresip", "Quiting")
if (BaresipService.IS_SERVICE_RUNNING) { if (BaresipService.isServiceRunning) {
baresipService.setAction("Stop"); baresipService.setAction("Stop");
startService(baresipService) startService(baresipService)
} }
@@ -876,11 +827,11 @@ class MainActivity : AppCompatActivity() {
ACCOUNTS_CODE -> { ACCOUNTS_CODE -> {
uaAdapter.notifyDataSetChanged() uaAdapter.notifyDataSetChanged()
if (uas.size > 0) { if (UserAgent.uas().size > 0) {
if ((aorSpinner.selectedItemPosition == -1) || if ((aorSpinner.selectedItemPosition == -1) ||
(aorSpinner.selectedItemPosition >= uas.size)) (aorSpinner.selectedItemPosition >= UserAgent.uas().size))
aorSpinner.setSelection(0) aorSpinner.setSelection(0)
val acc = uas[aorSpinner.selectedItemPosition].account val acc = UserAgent.uas()[aorSpinner.selectedItemPosition].account
if (acc.vmUri != "") { if (acc.vmUri != "") {
if (acc.vmNew > 0) if (acc.vmNew > 0)
voicemailButton.setImageResource(R.drawable.voicemail_new) voicemailButton.setImageResource(R.drawable.voicemail_new)
@@ -898,7 +849,7 @@ class MainActivity : AppCompatActivity() {
} }
ACCOUNT_CODE -> { ACCOUNT_CODE -> {
val acc = uas[aorSpinner.selectedItemPosition].account val acc = UserAgent.uas()[aorSpinner.selectedItemPosition].account
if (acc.vmUri != "") { if (acc.vmUri != "") {
if (acc.vmNew > 0) if (acc.vmNew > 0)
voicemailButton.setImageResource(R.drawable.voicemail_new) voicemailButton.setImageResource(R.drawable.voicemail_new)
@@ -912,14 +863,14 @@ class MainActivity : AppCompatActivity() {
CONTACTS_CODE -> { CONTACTS_CODE -> {
callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
ContactsActivity.contacts.map{Contact -> Contact.name})) Contact.contacts().map{Contact -> Contact.name}))
} }
EDIT_CONFIG_CODE -> { EDIT_CONFIG_CODE -> {
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
Utils.alertView(this, "Notice", Utils.alertView(this, "Notice",
"You need to restart baresip in order to activate saved config!") "You need to restart baresip in order to activate saved config!")
reload_config() Api.reload_config()
} }
if (resultCode == RESULT_CANCELED) { if (resultCode == RESULT_CANCELED) {
Log.d("Baresip", "Edit config canceled") Log.d("Baresip", "Edit config canceled")
@@ -927,14 +878,17 @@ class MainActivity : AppCompatActivity() {
} }
CALLS_CODE -> { CALLS_CODE -> {
val acc = uas[aorSpinner.selectedItemPosition].account val acc = UserAgent.uas()[aorSpinner.selectedItemPosition].account
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
if (data != null) if (data != null) {
callUri.setText(data.getStringExtra("peer_uri")) val peerUri = data.getStringExtra("peer_uri")
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(peerUri),
Utils.aorDomain(Utils.aorDomain(acc.aor))))
}
} }
if (resultCode == RESULT_CANCELED) { if (resultCode == RESULT_CANCELED) {
Log.d("Baresip", "History canceled") Log.d("Baresip", "History canceled")
if (CallHistory.aorHistory(history, acc.aor) == 0) { if (CallHistory.aorHistorySize(acc.aor) == 0) {
holdButton.visibility = View.INVISIBLE holdButton.visibility = View.INVISIBLE
} }
} }
@@ -946,6 +900,14 @@ class MainActivity : AppCompatActivity() {
} }
} }
private fun spinToAor(aor: String) {
for (account_index in UserAgent.uas().indices)
if (UserAgent.uas()[account_index].account.aor == aor) {
aorSpinner.setSelection(account_index)
break
}
}
private fun call(ua: UserAgent, uri: String) { private fun call(ua: UserAgent, uri: String) {
if (ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.RECORD_AUDIO) if (ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.RECORD_AUDIO)
== PackageManager.PERMISSION_DENIED) { == PackageManager.PERMISSION_DENIED) {
@@ -953,12 +915,16 @@ class MainActivity : AppCompatActivity() {
"You have not granted microphone permission.", Toast.LENGTH_SHORT).show() "You have not granted microphone permission.", Toast.LENGTH_SHORT).show()
return return
} }
callUri.setText(uri) if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition])
spinToAor(ua.account.aor)
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(uri),
Utils.aorDomain(Utils.aorDomain(ua.account.aor))))
callButton.visibility = View.INVISIBLE callButton.visibility = View.INVISIBLE
val callp = ua_connect(ua.uap, uri) val callp = Api.ua_connect(ua.uap, uri)
if (callp != "") { if (callp != "") {
Log.d("Baresip", "Adding outgoing call ${ua.uap}/$callp/$uri") Log.d("Baresip", "Adding outgoing call ${ua.uap}/$callp/$uri")
calls.add(Call(callp, ua, uri, "out", "outgoing", dtmfWatcher(callp))) Call.calls().add(Call(callp, ua, uri, "out", "outgoing",
Utils.dtmfWatcher(callp)))
imm.hideSoftInputFromWindow(callUri.windowToken, 0) imm.hideSoftInputFromWindow(callUri.windowToken, 0)
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
callButton.visibility = View.INVISIBLE callButton.visibility = View.INVISIBLE
@@ -975,44 +941,27 @@ class MainActivity : AppCompatActivity() {
} }
private fun transfer(ua: UserAgent, call: Call, uri: String) { private fun transfer(ua: UserAgent, call: Call, uri: String) {
val newCallp = ua_call_alloc(ua.uap, call.callp) val newCallp = Api.ua_call_alloc(ua.uap, call.callp)
if (newCallp != "") { if (newCallp != "") {
Log.d("Baresip", "Adding outgoing call ${ua.uap}/$newCallp/$uri") Log.d("Baresip", "Adding outgoing call ${ua.uap}/$newCallp/$uri")
val newCall = Call(newCallp, ua, uri, "out", "transferring", val newCall = Call(newCallp, ua, uri, "out", "transferring",
dtmfWatcher(newCallp)) Utils.dtmfWatcher(newCallp))
calls.add(newCall) Call.calls().add(newCall)
call_stop_audio(call.callp) Api.call_stop_audio(call.callp)
call_start_audio(newCallp) Api.call_start_audio(newCallp)
val err = call_connect(newCallp, uri) val err = Api.call_connect(newCallp, uri)
if (err == 0) { if (err == 0) {
if (ua == uas[aorSpinner.selectedItemPosition]) showCall(ua) if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition])
spinToAor(ua.account.aor)
showCall(ua)
} else { } else {
call_start_audio(call.callp) Api.call_start_audio(call.callp)
Log.w("Baresip", "call_connect $newCallp failed with error $err") Log.w("Baresip", "call_connect $newCallp failed with error $err")
call_notify_sipfrag(call.callp, 500, "Call Error") Api.call_notify_sipfrag(call.callp, 500, "Call Error")
} }
} else { } else {
Log.w("Baresip", "ua_call_alloc ${ua.uap}/${call.callp} failed") Log.w("Baresip", "ua_call_alloc ${ua.uap}/${call.callp} failed")
call_notify_sipfrag(call.callp, 500, "Call Error") Api.call_notify_sipfrag(call.callp, 500, "Call Error")
}
}
private fun dtmfWatcher(callp: String): TextWatcher {
return object : TextWatcher {
override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(sequence: CharSequence, start: Int, before: Int, count: Int) {
val text = sequence.subSequence(start, start + count).toString()
if (text.length > 0) {
val digit = text[0]
Log.d("Baresip", "Got DTMF digit '$digit'")
if (((digit >= '0') && (digit <= '9')) || (digit == '*') || (digit == '#'))
call_send_digit(callp, digit)
}
}
override fun afterTextChanged(sequence: Editable) {
// KEYCODE_REL
// call_send_digit(callp, 4.toChar())
}
} }
} }
@@ -1031,10 +980,13 @@ class MainActivity : AppCompatActivity() {
} }
private fun showCall(ua: UserAgent) { private fun showCall(ua: UserAgent) {
if (Call.uaCalls(calls, ua, "").size == 0) { if (Call.uaCalls(ua, "").size == 0) {
callTitle.text = "Outgoing call to ..." callTitle.text = "Outgoing call to ..."
callUri.text.clear() callUri.text.clear()
callUri.hint = "Callee" callUri.hint = "Callee"
callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
Contact.contacts().map{Contact -> Contact.name}))
imm.hideSoftInputFromWindow(callUri.windowToken, 0)
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
callButton.visibility = View.VISIBLE callButton.visibility = View.VISIBLE
hangupButton.visibility = View.INVISIBLE hangupButton.visibility = View.INVISIBLE
@@ -1043,8 +995,8 @@ class MainActivity : AppCompatActivity() {
holdButton.visibility = View.INVISIBLE holdButton.visibility = View.INVISIBLE
dtmf.visibility = View.INVISIBLE dtmf.visibility = View.INVISIBLE
} else { } else {
val callsOut = Call.uaCalls(calls, ua, "out") val callsOut = Call.uaCalls(ua, "out")
val callsIn = Call.uaCalls(calls, ua, "in") val callsIn = Call.uaCalls(ua, "in")
val call: Call val call: Call
if (callsOut.size > 0) { if (callsOut.size > 0) {
call = callsOut[callsOut.size - 1] call = callsOut[callsOut.size - 1]
@@ -1056,7 +1008,8 @@ class MainActivity : AppCompatActivity() {
callTitle.text = "Incoming call from ..." callTitle.text = "Incoming call from ..."
call = callsIn[callsIn.size - 1] call = callsIn[callsIn.size - 1]
} }
callUri.setText(call.peerURI) callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(call.peerURI),
Utils.aorDomain(ua.account.aor)))
when (call.status) { when (call.status) {
"outgoing", "transferring" -> { "outgoing", "transferring" -> {
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
@@ -1112,97 +1065,8 @@ class MainActivity : AppCompatActivity() {
} }
} }
private fun requestAudioFocus(streamType: Int) {
val res: Int
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) && (audioFocusRequest == null)) {
val playbackAttributes = AudioAttributes.Builder()
.setLegacyStreamType(streamType)
.build()
@TargetApi(26)
audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
.setAudioAttributes(playbackAttributes)
.build()
@TargetApi(26)
res = am.requestAudioFocus(audioFocusRequest)
if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d("Baresip", "Audio focus granted")
} else {
Log.d("Baresip", "Audio focus denied")
audioFocusRequest = null
}
} else {
res = am.requestAudioFocus(null, streamType,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d("Baresip", "Audio focus granted")
audioFocused = true
} else {
Log.d("Baresip", "Audio focus denied")
audioFocused = false
}
}
}
private fun abandonAudioFocus() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (audioFocusRequest != null) {
am.abandonAudioFocusRequest(audioFocusRequest)
audioFocusRequest = null
}
} else {
if (audioFocused) {
am.abandonAudioFocus(null)
audioFocused = false
}
}
}
private fun startRinging() {
am.mode = AudioManager.MODE_RINGTONE
requestAudioFocus(AudioManager.STREAM_RING)
rt.play()
rtTimer = Timer()
rtTimer!!.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
if (!rt.isPlaying()) {
rt.play()
}
}
}, 1000 * 1, 1000 * 1)
}
private fun stopRinging() {
if (rtTimer != null) {
rtTimer!!.cancel()
rtTimer = null
if (rt.isPlaying) rt.stop()
}
}
external fun uag_current_set(uap: String)
external fun ua_connect(uap: String, peer_uri: String): String
external fun ua_call_alloc(uap: String, xcallp: String): String
external fun ua_answer(uap: String, callp: String)
external fun ua_hangup(uap: String, callp: String, code: Int, reason: String)
external fun call_hold(callp: String): Int
external fun call_unhold(callp: String): Int
external fun call_send_digit(callp: String, digit: Char): Int
external fun call_connect(callp: String, peer_uri: String): Int
external fun call_notify_sipfrag(callp: String, code: Int, reason: String)
external fun call_start_audio(callp: String)
external fun call_stop_audio(callp: String)
external fun reload_config(): Int
companion object { companion object {
var uas = ArrayList<UserAgent>()
internal var images = ArrayList<Int>()
var history: ArrayList<CallHistory> = ArrayList()
internal var calls = ArrayList<Call>()
internal var messages = ArrayList<Message>()
internal var audioFocused = false
internal var audioFocusRequest: AudioFocusRequest? = null
var filesPath = ""
var visible = true var visible = true
var makeCall = "" var makeCall = ""
var answerCall = "" var answerCall = ""
@@ -1217,11 +1081,7 @@ class MainActivity : AppCompatActivity() {
const val CONTACT_CODE = 7 const val CONTACT_CODE = 7
const val MESSAGES_CODE = 8 const val MESSAGES_CODE = 8
const val MESSAGE_CODE = 9 const val MESSAGE_CODE = 9
const val RECORD_AUDIO_PERMISSION = 1 const val RECORD_AUDIO_PERMISSION = 1
const val HISTORY_SIZE = 100
const val MESSAGE_HISTORY_SIZE = 100
const val ONE_CALL_ONLY = true
} }
@@ -1,7 +1,38 @@
package com.tutpro.baresip package com.tutpro.baresip
import java.io.Serializable import java.io.*
import java.util.ArrayList
class Message(val aor: String, val peerUri: String, var direction: Int, val message: String, class Message(val aor: String, val peerUri: String, var direction: Int, val message: String,
val timeStamp: Long, var new: Boolean): Serializable { val timeStamp: Long, var new: Boolean): Serializable {
companion object {
val MESSAGE_HISTORY_SIZE = 100
fun messages(): ArrayList<Message> {
return BaresipService.messages
}
fun messages(messages: ArrayList<Message>) {
BaresipService.messages = messages
}
fun add(message: Message) {
BaresipService.messages.add(message)
var count = 0
var firstIndex = -1
for (i in BaresipService.messages.indices)
if (BaresipService.messages[i].aor == message.aor) {
if (count == 0) firstIndex = i
count++
if (count > MESSAGE_HISTORY_SIZE) {
break
}
}
if (count > MESSAGE_HISTORY_SIZE)
BaresipService.messages.removeAt(firstIndex)
}
}
} }
@@ -4,37 +4,51 @@ import android.util.Log
class UserAgent (val uap: String) { class UserAgent (val uap: String) {
val account = Account(ua_account(uap)) val account = Account(Api.ua_account(uap))
fun register() { fun register() {
if (account.regint > 0) { if (account.regint > 0) {
Log.d("Baresip", "Registering ${account.aor} UA ${uap}") Log.d("Baresip", "Registering ${account.aor} UA ${uap}")
if (ua_register(uap) != 0) if (Api.ua_register(uap) != 0)
Log.e("Baresip", "Registering failed") Log.e("Baresip", "Registering failed")
} }
} }
fun destroy() { fun destroy() {
ua_destroy(uap) Api.ua_destroy(uap)
} }
companion object { companion object {
external fun ua_alloc(uri: String): String fun uas(): ArrayList<UserAgent> {
external fun ua_update_account(ua: String): Int return BaresipService.uas
external fun ua_destroy(ua: String) }
external fun ua_register(ua: String): Int
external fun ua_isregistered(ua: String): Boolean fun status(): ArrayList<Int> {
external fun ua_unregister(ua: String) return BaresipService.status
}
fun add(ua: UserAgent, status: Int) {
BaresipService.uas.add(ua)
BaresipService.status.add(status)
}
fun remove(ua: UserAgent) {
val index = BaresipService.uas.indexOf(ua)
if (index != -1) {
BaresipService.uas[index].destroy()
BaresipService.status.removeAt(index)
}
}
fun uaAlloc(uri: String): UserAgent? { fun uaAlloc(uri: String): UserAgent? {
val uap = ua_alloc(uri) val uap = Api.ua_alloc(uri)
if (uap != "") return UserAgent(uap) if (uap != "") return UserAgent(uap)
return null return null
} }
fun find(uas: ArrayList<UserAgent>, uap: String): UserAgent? { fun find(uap: String): UserAgent? {
for (ua in uas) { for (ua in BaresipService.uas) {
if (ua.uap == uap) return ua if (ua.uap == uap) return ua
} }
return null return null
@@ -50,12 +64,10 @@ class UserAgent (val uap: String) {
fun register(uas: ArrayList<UserAgent>) { fun register(uas: ArrayList<UserAgent>) {
for (ua in uas) { for (ua in uas) {
if (ua.account.regint > 0) if (ua.account.regint > 0)
if (ua_register(ua.uap) != 0) if (Api.ua_register(ua.uap) != 0)
Log.e("Baresip", "Failed to register ${ua.account.aor}") Log.e("Baresip", "Failed to register ${ua.account.aor}")
} }
} }
} }
} }
external fun ua_account(ua: String): String
@@ -6,6 +6,8 @@ import android.support.v7.app.AlertDialog
import android.util.Log import android.util.Log
import android.os.PowerManager import android.os.PowerManager
import android.app.KeyguardManager import android.app.KeyguardManager
import android.text.Editable
import android.text.TextWatcher
import java.io.* import java.io.*
@@ -95,9 +97,13 @@ object Utils {
} }
fun friendlyUri(uri: String, domain: String): String { fun friendlyUri(uri: String, domain: String): String {
if (uri.contains("@")) {
val user = uriUserPart(uri) val user = uriUserPart(uri)
val host = uriHostPart(uri) val host = uriHostPart(uri)
if (host == domain) return user else return "$user@$host" if (host == domain) return user else return "$user@$host"
} else {
return uri
}
} }
fun aorDomain(aor: String): String { fun aorDomain(aor: String): String {
@@ -233,4 +239,23 @@ object Utils {
return isLocked return isLocked
} }
fun dtmfWatcher(callp: String): TextWatcher {
return object : TextWatcher {
override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(sequence: CharSequence, start: Int, before: Int, count: Int) {
val text = sequence.subSequence(start, start + count).toString()
if (text.length > 0) {
val digit = text[0]
Log.d("Baresip", "Got DTMF digit '$digit'")
if (((digit >= '0') && (digit <= '9')) || (digit == '*') || (digit == '#'))
Api.call_send_digit(callp, digit)
}
}
override fun afterTextChanged(sequence: Editable) {
// KEYCODE_REL
// call_send_digit(callp, 4.toChar())
}
}
}
} }
+1
View File
@@ -26,6 +26,7 @@
android:textAlignment="gravity" android:textAlignment="gravity"
android:paddingTop="5dp" android:paddingTop="5dp"
android:paddingBottom="5dp" android:paddingBottom="5dp"
android:paddingEnd="5dp"
android:textSize="18sp" android:textSize="18sp"
android:maxLines="1" android:maxLines="1"
android:text="" /> android:text="" />